vysion 1.0.11__tar.gz → 1.0.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vysion-1.0.11 → vysion-1.0.13}/PKG-INFO +3 -3
- {vysion-1.0.11 → vysion-1.0.13}/README.md +2 -2
- {vysion-1.0.11 → vysion-1.0.13}/pyproject.toml +1 -1
- {vysion-1.0.11 → vysion-1.0.13}/vysion/dto/dto.py +127 -24
- {vysion-1.0.11 → vysion-1.0.13}/LICENSE +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/__init__.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/client/__init__.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/client/client.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/client/error.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/dto/__init__.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/dto/tag.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/dto/util.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/model/__init__.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/model/enum/__init__.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/model/enum/languages.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/model/enum/networks.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/model/enum/ransom_groups.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/model/enum/services.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/model/model.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/taxonomy/__init__.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/taxonomy/flavours.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/taxonomy/taxonomy.py +0 -0
- {vysion-1.0.11 → vysion-1.0.13}/vysion/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vysion
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.13
|
|
4
4
|
Summary: The official Python client library for Vysion
|
|
5
5
|
Home-page: https://vysion.ai
|
|
6
6
|
License: Apache-2.0
|
|
@@ -25,11 +25,11 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
|
|
26
26
|
# Vysion-PY
|
|
27
27
|
|
|
28
|
-
Welcome to the PyPi webpage for Vysion, our implementation as a Python library to use the Vysion tool
|
|
28
|
+
Welcome to the PyPi webpage for Vysion, our implementation as a Python library to use the Vysion tool Vysion is a dark web intelligence tool that provides information collected from web pages from Tor, I2P, cybercrime forums on the clearnet, etc. Vysion API also provides a feed of information on ransomware attacks published by the various ransomware groups currently active.
|
|
29
29
|
|
|
30
30
|
You can request a demo for the web app or an API-key to use in this library at [vysion.ai](https://vysion.ai).
|
|
31
31
|
|
|
32
|
-
Latest version: [1.0.
|
|
32
|
+
Latest version: [1.0.13](https://pypi.org/project/vysion/)
|
|
33
33
|
|
|
34
34
|
You can visit [the documentation](https://developers.vysion.ai/?python) for more information on the searches and requests performed with the library or directly on the API.
|
|
35
35
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Vysion-PY
|
|
2
2
|
|
|
3
|
-
Welcome to the PyPi webpage for Vysion, our implementation as a Python library to use the Vysion tool
|
|
3
|
+
Welcome to the PyPi webpage for Vysion, our implementation as a Python library to use the Vysion tool Vysion is a dark web intelligence tool that provides information collected from web pages from Tor, I2P, cybercrime forums on the clearnet, etc. Vysion API also provides a feed of information on ransomware attacks published by the various ransomware groups currently active.
|
|
4
4
|
|
|
5
5
|
You can request a demo for the web app or an API-key to use in this library at [vysion.ai](https://vysion.ai).
|
|
6
6
|
|
|
7
|
-
Latest version: [1.0.
|
|
7
|
+
Latest version: [1.0.13](https://pypi.org/project/vysion/)
|
|
8
8
|
|
|
9
9
|
You can visit [the documentation](https://developers.vysion.ai/?python) for more information on the searches and requests performed with the library or directly on the API.
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Copyright
|
|
3
|
+
Copyright 2024 ByronLabs S.L.
|
|
4
4
|
|
|
5
5
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
you may not use this file except in compliance with the License.
|
|
@@ -17,6 +17,7 @@ limitations under the License.
|
|
|
17
17
|
import hashlib
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
from enum import Enum
|
|
20
|
+
import re
|
|
20
21
|
|
|
21
22
|
from vysion.model import enum
|
|
22
23
|
from vysion.taxonomy import Monero_Address, Ripple_Address
|
|
@@ -29,7 +30,15 @@ except:
|
|
|
29
30
|
from typing import List, Optional, Union
|
|
30
31
|
from urllib.parse import urlparse
|
|
31
32
|
|
|
32
|
-
from pydantic import
|
|
33
|
+
from pydantic import (
|
|
34
|
+
BaseModel,
|
|
35
|
+
Field,
|
|
36
|
+
ConfigDict,
|
|
37
|
+
validator,
|
|
38
|
+
field_validator,
|
|
39
|
+
root_validator,
|
|
40
|
+
)
|
|
41
|
+
from pydantic_core.core_schema import FieldValidationInfo
|
|
33
42
|
|
|
34
43
|
from vysion import taxonomy as vystaxonomy
|
|
35
44
|
from vysion.model import URL as URL_model
|
|
@@ -39,7 +48,6 @@ from .tag import *
|
|
|
39
48
|
|
|
40
49
|
|
|
41
50
|
class Email(BaseModel):
|
|
42
|
-
|
|
43
51
|
_taxonomy = [vystaxonomy.Email]
|
|
44
52
|
|
|
45
53
|
# RFC 5322 Official Standard (https://www.emailregex.com/)
|
|
@@ -48,77 +56,66 @@ class Email(BaseModel):
|
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
class Paste(BaseModel):
|
|
51
|
-
|
|
52
59
|
_taxonomy = [vystaxonomy.Pastebin, vystaxonomy.JustPaste]
|
|
53
60
|
|
|
54
61
|
value: str # TODO Regex
|
|
55
62
|
|
|
56
63
|
|
|
57
64
|
class Skype(BaseModel):
|
|
58
|
-
|
|
59
65
|
_taxonomy = [vystaxonomy.Skype]
|
|
60
66
|
|
|
61
67
|
value: str # TODO Regex
|
|
62
68
|
|
|
63
69
|
|
|
64
70
|
class Telegram(BaseModel):
|
|
65
|
-
|
|
66
71
|
_taxonomy = [vystaxonomy.Telegram] # TODO Create Telegram URL
|
|
67
72
|
|
|
68
73
|
value: str # TODO Regex
|
|
69
74
|
|
|
70
75
|
|
|
71
76
|
class BitcoinAddress(BaseModel):
|
|
72
|
-
|
|
73
77
|
_taxonomy = [vystaxonomy.Bitcoin_Address] # TODO Create Telegram URL
|
|
74
78
|
|
|
75
79
|
value: str # TODO Regex
|
|
76
80
|
|
|
77
81
|
|
|
78
82
|
class PolkadotAddress(BaseModel):
|
|
79
|
-
|
|
80
83
|
_taxonomy = [vystaxonomy.Polkadot_Address] # TODO Create Telegram URL
|
|
81
84
|
|
|
82
85
|
value: str # TODO Regex
|
|
83
86
|
|
|
84
87
|
|
|
85
88
|
class EthereumAddress(BaseModel):
|
|
86
|
-
|
|
87
89
|
_taxonomy = [vystaxonomy.Ethereum_Address] # TODO Create Telegram URL
|
|
88
90
|
|
|
89
91
|
value: str # TODO Regex
|
|
90
92
|
|
|
91
93
|
|
|
92
94
|
class MoneroAddress(BaseModel):
|
|
93
|
-
|
|
94
95
|
_taxonomy = [vystaxonomy.Monero_Address] # TODO Create Telegram URL
|
|
95
96
|
|
|
96
97
|
value: str # TODO Regex
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
class RippleAddress(BaseModel):
|
|
100
|
-
|
|
101
101
|
_taxonomy = [vystaxonomy.Ripple_Address] # TODO Create Telegram URL
|
|
102
102
|
|
|
103
103
|
value: str # TODO Regex
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
class ZcashAddress(BaseModel):
|
|
107
|
-
|
|
108
107
|
_taxonomy = [vystaxonomy.Zcash_Address] # TODO Create Telegram URL
|
|
109
108
|
|
|
110
109
|
value: str # TODO Regex
|
|
111
110
|
|
|
112
111
|
|
|
113
112
|
class WhatsApp(BaseModel):
|
|
114
|
-
|
|
115
113
|
_taxonomy = [vystaxonomy.WhatsApp]
|
|
116
114
|
|
|
117
115
|
value: str # TODO Regex
|
|
118
116
|
|
|
119
117
|
|
|
120
118
|
class URL(BaseModel):
|
|
121
|
-
|
|
122
119
|
_taxonomy = [vystaxonomy.URL]
|
|
123
120
|
|
|
124
121
|
protocol: Optional[str]
|
|
@@ -132,7 +129,6 @@ class URL(BaseModel):
|
|
|
132
129
|
|
|
133
130
|
@classmethod
|
|
134
131
|
def parse(cls, url):
|
|
135
|
-
|
|
136
132
|
# TODO Save this parsed in a private variable? (e.g., _pared_)
|
|
137
133
|
parsed = URL_model.parse(url)
|
|
138
134
|
print(parsed)
|
|
@@ -164,7 +160,7 @@ class Page(BaseModel):
|
|
|
164
160
|
chunk: bool = False
|
|
165
161
|
|
|
166
162
|
|
|
167
|
-
class
|
|
163
|
+
class RansomwareHit(BaseModel):
|
|
168
164
|
id: str
|
|
169
165
|
url: URL
|
|
170
166
|
# title: str = None
|
|
@@ -175,7 +171,7 @@ class RansomwarePage(BaseModel):
|
|
|
175
171
|
info: Optional[str]
|
|
176
172
|
html: Optional[str]
|
|
177
173
|
country: Optional[str]
|
|
178
|
-
sha256sum: str = None
|
|
174
|
+
sha256sum: Optional[str] = None
|
|
179
175
|
ssdeep: Optional[str] = None
|
|
180
176
|
date: datetime
|
|
181
177
|
chunk: bool = False
|
|
@@ -183,9 +179,113 @@ class RansomwarePage(BaseModel):
|
|
|
183
179
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
184
180
|
|
|
185
181
|
|
|
186
|
-
class
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
class Media(BaseModel):
|
|
183
|
+
bucketName: Optional[str] = Field(default_factory=lambda: None)
|
|
184
|
+
objectPath: Optional[str] = Field(default_factory=lambda: None)
|
|
185
|
+
objectName: Optional[str] = Field(default_factory=lambda: None)
|
|
186
|
+
contentType: str
|
|
187
|
+
|
|
188
|
+
@validator("bucketName", "objectPath", "objectName")
|
|
189
|
+
def validate_strings(cls, v):
|
|
190
|
+
if v is not None and not isinstance(v, str):
|
|
191
|
+
raise ValueError("value must be a string")
|
|
192
|
+
return v
|
|
193
|
+
|
|
194
|
+
@field_validator("contentType")
|
|
195
|
+
def validate_contentType(cls, v: str) -> str:
|
|
196
|
+
if not v.strip():
|
|
197
|
+
raise ValueError("contentType field cannot be empty")
|
|
198
|
+
return v
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class LanguagePair(BaseModel):
|
|
202
|
+
language: str
|
|
203
|
+
probability: float
|
|
204
|
+
|
|
205
|
+
@root_validator(pre=True)
|
|
206
|
+
def split_key_value(cls, value: str) -> dict:
|
|
207
|
+
if isinstance(value, str):
|
|
208
|
+
key, value = value.split(":")
|
|
209
|
+
return {"key": key, "value": float(value)}
|
|
210
|
+
return value
|
|
211
|
+
|
|
212
|
+
@field_validator("language")
|
|
213
|
+
def validate_language(cls, v: str) -> str:
|
|
214
|
+
if not v.strip():
|
|
215
|
+
raise ValueError("Language field cannot be empty")
|
|
216
|
+
return v
|
|
217
|
+
|
|
218
|
+
@field_validator("probability")
|
|
219
|
+
def validate_probability(cls, v: float) -> float:
|
|
220
|
+
if v < 0 or v > 1:
|
|
221
|
+
raise ValueError("Probability must be between 0 and 1")
|
|
222
|
+
return v
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class TelegramHit(BaseModel):
|
|
226
|
+
userId: Optional[int] = Field(default_factory=lambda: None)
|
|
227
|
+
username: Optional[str] = Field(default_factory=lambda: None)
|
|
228
|
+
channelId: Optional[int] = Field(default_factory=lambda: None)
|
|
229
|
+
messageId: int
|
|
230
|
+
message: Optional[str] = Field(default_factory=lambda: None)
|
|
231
|
+
channelTitle: Optional[str] = Field(default_factory=lambda: None)
|
|
232
|
+
languages: Optional[List[LanguagePair]] = Field(default_factory=lambda: None)
|
|
233
|
+
sha1sum: Optional[str] = None
|
|
234
|
+
sha256sum: Optional[str] = None
|
|
235
|
+
media: Optional[str] = Field(default_factory=lambda: None)
|
|
236
|
+
detectionDate: datetime
|
|
237
|
+
|
|
238
|
+
@field_validator("messageId")
|
|
239
|
+
def validate_messageId(cls, v: int) -> int:
|
|
240
|
+
if not v:
|
|
241
|
+
raise ValueError("MessageId field cannot be empty")
|
|
242
|
+
return v
|
|
243
|
+
|
|
244
|
+
class TelegramProfileHit(BaseModel):
|
|
245
|
+
userId: int
|
|
246
|
+
usernames: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
247
|
+
firstName: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
248
|
+
lastName: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
249
|
+
detectionDate: datetime
|
|
250
|
+
profilePhoto: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
251
|
+
|
|
252
|
+
@field_validator("userId")
|
|
253
|
+
def validate_userId(cls, v: int) -> int:
|
|
254
|
+
if not v:
|
|
255
|
+
raise ValueError("UserId field cannot be empty")
|
|
256
|
+
return v
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@field_validator("detectionDate")
|
|
260
|
+
def validate_detectionDate(cls, v: datetime) -> datetime:
|
|
261
|
+
if not v:
|
|
262
|
+
raise ValueError("DetectionDate field cannot be empty")
|
|
263
|
+
return v
|
|
264
|
+
|
|
265
|
+
class TelegramChannelHit(BaseModel):
|
|
266
|
+
channelId: int
|
|
267
|
+
channelTitles: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
268
|
+
detectionDate: datetime
|
|
269
|
+
creationDate: datetime
|
|
270
|
+
channelPhoto: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
271
|
+
|
|
272
|
+
@field_validator("channelId")
|
|
273
|
+
def validate_channelId(cls, v: int) -> int:
|
|
274
|
+
if not v:
|
|
275
|
+
raise ValueError("channelId field cannot be empty")
|
|
276
|
+
return v
|
|
277
|
+
|
|
278
|
+
@field_validator("detectionDate")
|
|
279
|
+
def validate_detectionDate(cls, v: datetime) -> datetime:
|
|
280
|
+
if not v:
|
|
281
|
+
raise ValueError("DetectionDate field cannot be empty")
|
|
282
|
+
return v
|
|
283
|
+
|
|
284
|
+
@field_validator("creationDate")
|
|
285
|
+
def validate_creationDate(cls, v: datetime) -> datetime:
|
|
286
|
+
if not v:
|
|
287
|
+
raise ValueError("creationDate field cannot be empty")
|
|
288
|
+
return v
|
|
189
289
|
|
|
190
290
|
class Hit(BaseModel):
|
|
191
291
|
page: Page
|
|
@@ -217,7 +317,6 @@ class RansomFeedHit(BaseModel):
|
|
|
217
317
|
|
|
218
318
|
|
|
219
319
|
class TelegramFeedHit(BaseModel):
|
|
220
|
-
|
|
221
320
|
id: str
|
|
222
321
|
telegram: List[str]
|
|
223
322
|
date: datetime
|
|
@@ -230,7 +329,13 @@ class Result(BaseModel):
|
|
|
230
329
|
# TODO Add pagination, query, etc?
|
|
231
330
|
total: int = 0
|
|
232
331
|
hits: Union[
|
|
233
|
-
List[Hit],
|
|
332
|
+
List[Hit],
|
|
333
|
+
List[TelegramHit],
|
|
334
|
+
List[RansomFeedHit],
|
|
335
|
+
List[TelegramFeedHit],
|
|
336
|
+
List[RansomwareHit],
|
|
337
|
+
List[TelegramProfileHit],
|
|
338
|
+
List[TelegramChannelHit]
|
|
234
339
|
] = Field(default_factory=lambda: [])
|
|
235
340
|
|
|
236
341
|
def __init__(self, **kwargs):
|
|
@@ -239,7 +344,6 @@ class Result(BaseModel):
|
|
|
239
344
|
self.total = len(self.hits)
|
|
240
345
|
|
|
241
346
|
def get_type(self) -> type:
|
|
242
|
-
|
|
243
347
|
if len(self.hits) <= 0:
|
|
244
348
|
return NoneType
|
|
245
349
|
|
|
@@ -257,7 +361,6 @@ class VysionResponse(BaseModel):
|
|
|
257
361
|
|
|
258
362
|
class VysionError(BaseModel):
|
|
259
363
|
class StatusCode(int, Enum):
|
|
260
|
-
|
|
261
364
|
UNK = 000
|
|
262
365
|
OK = 200
|
|
263
366
|
REQ_ERROR = 400
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|