vysion 1.0.16__py3-none-any.whl → 2.0.1__py3-none-any.whl
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/client/client.py +151 -157
- vysion/client/error.py +3 -3
- vysion/dto/dto.py +205 -123
- vysion/dto/util.py +22 -21
- vysion/model/__init__.py +0 -1
- vysion/model/enum/languages.py +0 -1
- vysion/model/enum/networks.py +0 -1
- vysion/model/enum/ransom_groups.py +0 -1
- vysion/model/enum/services.py +0 -1
- vysion/taxonomy/taxonomy.py +0 -2
- vysion/version.py +1 -1
- {vysion-1.0.16.dist-info → vysion-2.0.1.dist-info}/METADATA +2 -2
- vysion-2.0.1.dist-info/RECORD +22 -0
- vysion/model/model.py +0 -184
- vysion-1.0.16.dist-info/RECORD +0 -23
- {vysion-1.0.16.dist-info → vysion-2.0.1.dist-info}/LICENSE +0 -0
- {vysion-1.0.16.dist-info → vysion-2.0.1.dist-info}/WHEEL +0 -0
vysion/dto/dto.py
CHANGED
|
@@ -14,37 +14,25 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
14
14
|
See the License for the specific language governing permissions and
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
from enum import Enum
|
|
20
|
-
import re
|
|
21
|
-
|
|
22
|
-
from vysion.model import enum
|
|
23
|
-
from vysion.taxonomy import Monero_Address, Ripple_Address
|
|
24
20
|
|
|
25
21
|
try:
|
|
26
22
|
from types import NoneType
|
|
27
23
|
except:
|
|
28
24
|
NoneType: type = type(None)
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
import uuid
|
|
27
|
+
from typing import Generic, List, Optional, TypeVar, Union
|
|
31
28
|
from urllib.parse import urlparse
|
|
32
29
|
|
|
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
|
|
30
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
42
31
|
|
|
43
32
|
from vysion import taxonomy as vystaxonomy
|
|
44
|
-
from vysion.model import
|
|
45
|
-
from vysion.model.enum import Language, Network, RansomGroup, Services
|
|
33
|
+
from vysion.model.enum import Language, Network
|
|
46
34
|
|
|
47
|
-
from .tag import
|
|
35
|
+
from .tag import Tag
|
|
48
36
|
|
|
49
37
|
|
|
50
38
|
class Email(BaseModel):
|
|
@@ -118,65 +106,138 @@ class WhatsApp(BaseModel):
|
|
|
118
106
|
class URL(BaseModel):
|
|
119
107
|
_taxonomy = [vystaxonomy.URL]
|
|
120
108
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
109
|
+
url: str
|
|
110
|
+
networkProtocol: str = Field(default_factory=lambda: "http")
|
|
111
|
+
domainName: str = Field(default_factory=lambda: None)
|
|
112
|
+
port: int = Field(default_factory=lambda: 80)
|
|
113
|
+
path: str = Field(default_factory=lambda: None)
|
|
114
|
+
signature: uuid.UUID = Field(
|
|
115
|
+
default_factory=lambda: uuid.UUID("{00000000-0000-0000-0000-000000000000}")
|
|
116
|
+
)
|
|
126
117
|
network: Network = Field(default_factory=lambda: Network.clearnet)
|
|
127
118
|
|
|
128
|
-
|
|
119
|
+
def __generate_signature(self) -> uuid.UUID:
|
|
120
|
+
return uuid.uuid5(uuid.NAMESPACE_URL, self.build())
|
|
129
121
|
|
|
130
122
|
@classmethod
|
|
131
123
|
def parse(cls, url):
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
124
|
+
parsed = urlparse(url)
|
|
125
|
+
|
|
126
|
+
# Elements
|
|
127
|
+
scheme = parsed.scheme
|
|
128
|
+
netloc = parsed.netloc
|
|
129
|
+
path = parsed.path
|
|
130
|
+
query = parsed.query
|
|
131
|
+
fragment = parsed.fragment
|
|
132
|
+
|
|
133
|
+
# Build domain:port
|
|
134
|
+
try:
|
|
135
|
+
domain_port = (netloc.split(":") + [80])[:2]
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(e)
|
|
138
|
+
|
|
139
|
+
domainName = domain_port[0]
|
|
140
|
+
|
|
141
|
+
if ".onion" in domainName:
|
|
142
|
+
network = Network.tor
|
|
143
|
+
elif ".i2p" in domainName:
|
|
144
|
+
network = Network.i2p
|
|
145
|
+
else:
|
|
146
|
+
network = Network.clearnet
|
|
147
|
+
|
|
148
|
+
port = domain_port[1]
|
|
149
|
+
|
|
150
|
+
# Build /path?query#fragment
|
|
151
|
+
res_path = path
|
|
152
|
+
|
|
153
|
+
# Rebuild path's query
|
|
154
|
+
if len(query) > 0:
|
|
155
|
+
query_parts = [param.split("=") for param in query.split("&")]
|
|
156
|
+
query_dict = {}
|
|
157
|
+
for part in query_parts:
|
|
158
|
+
if len(part) <= 1:
|
|
159
|
+
query_dict[part[0]] = str()
|
|
160
|
+
else:
|
|
161
|
+
query_dict[part[0]] = part[1]
|
|
162
|
+
|
|
163
|
+
query_keys = list(query_dict.keys())
|
|
164
|
+
query_keys.sort()
|
|
165
|
+
res_query_parts = [f"{k}={query_dict[k]}" for k in query_keys]
|
|
166
|
+
|
|
167
|
+
res_query = "?" + "&".join(res_query_parts)
|
|
168
|
+
|
|
169
|
+
res_path += res_query
|
|
170
|
+
|
|
171
|
+
if len(fragment) > 0:
|
|
172
|
+
res_path += f"#{fragment}"
|
|
173
|
+
|
|
174
|
+
return cls(
|
|
175
|
+
url=url,
|
|
176
|
+
networkProtocol=scheme,
|
|
177
|
+
domainName=domainName,
|
|
178
|
+
port=port,
|
|
179
|
+
path=res_path,
|
|
180
|
+
network=network,
|
|
141
181
|
)
|
|
142
182
|
|
|
143
|
-
|
|
183
|
+
def __init_subclass__(self, *args, **kwargs):
|
|
184
|
+
super().__init__(*args, **kwargs)
|
|
185
|
+
self.urlSignature = self.__generate_signature()
|
|
144
186
|
|
|
145
187
|
def build(self) -> str:
|
|
146
|
-
|
|
188
|
+
url = self.path
|
|
189
|
+
|
|
190
|
+
if self.domainName != "":
|
|
191
|
+
if self.port != 80:
|
|
192
|
+
url = f":{self.port}" + url
|
|
193
|
+
|
|
194
|
+
url = f"{self.domainName}" + url
|
|
195
|
+
|
|
196
|
+
if self.networkProtocol != "":
|
|
197
|
+
url = f"{self.networkProtocol}://" + url
|
|
198
|
+
|
|
199
|
+
return url
|
|
147
200
|
|
|
148
201
|
|
|
149
202
|
class Page(BaseModel):
|
|
150
203
|
id: str
|
|
151
204
|
url: URL
|
|
152
|
-
|
|
153
|
-
|
|
205
|
+
foundAt: Optional[str] = None
|
|
206
|
+
pageTitle: Optional[str] = None
|
|
154
207
|
language: Optional[Language]
|
|
155
|
-
html: str = None
|
|
208
|
+
html: Optional[str] = None
|
|
209
|
+
text: Optional[str] = None
|
|
156
210
|
sha1sum: Optional[str] = None
|
|
157
211
|
sha256sum: Optional[str] = None
|
|
158
212
|
ssdeep: Optional[str] = None
|
|
159
|
-
|
|
213
|
+
detectionDate: datetime = None
|
|
160
214
|
chunk: bool = False
|
|
161
215
|
|
|
162
216
|
|
|
163
217
|
class RansomwareHit(BaseModel):
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
info: Optional[str]
|
|
172
|
-
html: Optional[str]
|
|
173
|
-
country: Optional[str]
|
|
174
|
-
sha256sum: Optional[str] = None
|
|
175
|
-
ssdeep: Optional[str] = None
|
|
176
|
-
date: datetime
|
|
177
|
-
chunk: bool = False
|
|
218
|
+
page: Page
|
|
219
|
+
tag: List[Tag] = Field(default_factory=lambda: [])
|
|
220
|
+
ransomwareGroup: str
|
|
221
|
+
companyName: Optional[str] = None
|
|
222
|
+
companyAddress: Optional[str] = None
|
|
223
|
+
companyLink: Optional[str] = None
|
|
224
|
+
country: Optional[str] = None
|
|
178
225
|
|
|
179
|
-
|
|
226
|
+
|
|
227
|
+
class DocumentHit(BaseModel):
|
|
228
|
+
page: Page
|
|
229
|
+
tag: List[Tag] = Field(default_factory=lambda: [])
|
|
230
|
+
email: List[Email] = Field(default_factory=lambda: [])
|
|
231
|
+
paste: List[Paste] = Field(default_factory=lambda: [])
|
|
232
|
+
skype: List[Skype] = Field(default_factory=lambda: [])
|
|
233
|
+
telegram: List[Telegram] = Field(default_factory=lambda: [])
|
|
234
|
+
whatsapp: List[WhatsApp] = Field(default_factory=lambda: [])
|
|
235
|
+
bitcoin_address: List[BitcoinAddress] = Field(default_factory=lambda: [])
|
|
236
|
+
polkadot_address: List[PolkadotAddress] = Field(default_factory=lambda: [])
|
|
237
|
+
ethereum_address: List[EthereumAddress] = Field(default_factory=lambda: [])
|
|
238
|
+
monero_address: List[MoneroAddress] = Field(default_factory=lambda: [])
|
|
239
|
+
ripple_address: List[RippleAddress] = Field(default_factory=lambda: [])
|
|
240
|
+
zcash_address: List[ZcashAddress] = Field(default_factory=lambda: [])
|
|
180
241
|
|
|
181
242
|
|
|
182
243
|
class Media(BaseModel):
|
|
@@ -185,7 +246,7 @@ class Media(BaseModel):
|
|
|
185
246
|
objectName: Optional[str] = Field(default_factory=lambda: None)
|
|
186
247
|
contentType: str
|
|
187
248
|
|
|
188
|
-
@
|
|
249
|
+
@field_validator("bucketName", "objectPath", "objectName")
|
|
189
250
|
def validate_strings(cls, v):
|
|
190
251
|
if v is not None and not isinstance(v, str):
|
|
191
252
|
raise ValueError("value must be a string")
|
|
@@ -202,12 +263,13 @@ class LanguagePair(BaseModel):
|
|
|
202
263
|
language: str
|
|
203
264
|
probability: float
|
|
204
265
|
|
|
205
|
-
@
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
266
|
+
@model_validator(mode="before")
|
|
267
|
+
@classmethod
|
|
268
|
+
def split_key_value(cls, data: str) -> dict:
|
|
269
|
+
if isinstance(data, str):
|
|
270
|
+
key, value = data.split(":")
|
|
271
|
+
return {"language": key, "probability": float(value)}
|
|
272
|
+
return data
|
|
211
273
|
|
|
212
274
|
@field_validator("language")
|
|
213
275
|
def validate_language(cls, v: str) -> str:
|
|
@@ -222,11 +284,11 @@ class LanguagePair(BaseModel):
|
|
|
222
284
|
return v
|
|
223
285
|
|
|
224
286
|
|
|
225
|
-
class
|
|
287
|
+
class ImMessageHit(BaseModel):
|
|
226
288
|
userId: Optional[int] = Field(default_factory=lambda: None)
|
|
227
289
|
username: Optional[str] = Field(default_factory=lambda: None)
|
|
228
290
|
channelId: Optional[int] = Field(default_factory=lambda: None)
|
|
229
|
-
messageId:
|
|
291
|
+
messageId: str
|
|
230
292
|
message: Optional[str] = Field(default_factory=lambda: None)
|
|
231
293
|
channelTitle: Optional[str] = Field(default_factory=lambda: None)
|
|
232
294
|
languages: Optional[List[LanguagePair]] = Field(default_factory=lambda: None)
|
|
@@ -241,7 +303,8 @@ class TelegramHit(BaseModel):
|
|
|
241
303
|
raise ValueError("MessageId field cannot be empty")
|
|
242
304
|
return v
|
|
243
305
|
|
|
244
|
-
|
|
306
|
+
|
|
307
|
+
class ImProfileHit(BaseModel):
|
|
245
308
|
userId: int
|
|
246
309
|
usernames: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
247
310
|
firstName: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
@@ -255,18 +318,18 @@ class TelegramProfileHit(BaseModel):
|
|
|
255
318
|
raise ValueError("UserId field cannot be empty")
|
|
256
319
|
return v
|
|
257
320
|
|
|
258
|
-
|
|
259
321
|
@field_validator("detectionDate")
|
|
260
322
|
def validate_detectionDate(cls, v: datetime) -> datetime:
|
|
261
323
|
if not v:
|
|
262
324
|
raise ValueError("DetectionDate field cannot be empty")
|
|
263
325
|
return v
|
|
264
|
-
|
|
265
|
-
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class ImChannelHit(BaseModel):
|
|
266
329
|
channelId: int
|
|
267
330
|
channelTitles: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
268
331
|
detectionDate: datetime
|
|
269
|
-
creationDate: datetime
|
|
332
|
+
creationDate: datetime
|
|
270
333
|
channelPhoto: Optional[List[str]] = Field(default_factory=lambda: None)
|
|
271
334
|
|
|
272
335
|
@field_validator("channelId")
|
|
@@ -280,63 +343,63 @@ class TelegramChannelHit(BaseModel):
|
|
|
280
343
|
if not v:
|
|
281
344
|
raise ValueError("DetectionDate field cannot be empty")
|
|
282
345
|
return v
|
|
283
|
-
|
|
346
|
+
|
|
284
347
|
@field_validator("creationDate")
|
|
285
348
|
def validate_creationDate(cls, v: datetime) -> datetime:
|
|
286
349
|
if not v:
|
|
287
350
|
raise ValueError("creationDate field cannot be empty")
|
|
288
351
|
return v
|
|
289
352
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
bitcoin_address: List[BitcoinAddress] = Field(default_factory=lambda: [])
|
|
299
|
-
polkadot_address: List[PolkadotAddress] = Field(default_factory=lambda: [])
|
|
300
|
-
ethereum_address: List[EthereumAddress] = Field(default_factory=lambda: [])
|
|
301
|
-
monero_address: List[MoneroAddress] = Field(default_factory=lambda: [])
|
|
302
|
-
ripple_address: List[RippleAddress] = Field(default_factory=lambda: [])
|
|
303
|
-
zcash_address: List[ZcashAddress] = Field(default_factory=lambda: [])
|
|
353
|
+
|
|
354
|
+
class ImFeedHit(BaseModel):
|
|
355
|
+
id: str
|
|
356
|
+
telegram: List[str]
|
|
357
|
+
detectionDate: datetime
|
|
358
|
+
url: str
|
|
359
|
+
path: str
|
|
360
|
+
network: str
|
|
304
361
|
|
|
305
362
|
|
|
306
363
|
class RansomFeedHit(BaseModel):
|
|
307
364
|
id: str
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
365
|
+
companyName: Optional[str]
|
|
366
|
+
companyLink: Optional[str]
|
|
367
|
+
url: str
|
|
368
|
+
ransomwareGroup: str
|
|
369
|
+
detectionDate: datetime
|
|
370
|
+
text: Optional[str]
|
|
314
371
|
country: Optional[str]
|
|
315
372
|
|
|
316
373
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
317
374
|
|
|
318
375
|
|
|
319
|
-
class
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
376
|
+
class Stat(BaseModel):
|
|
377
|
+
key: Union[str, int]
|
|
378
|
+
doc_count: int
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class Buckets(BaseModel):
|
|
382
|
+
buckets: List[Stat]
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class AggStats(Stat):
|
|
386
|
+
key_as_string: str
|
|
387
|
+
agg: Optional[Buckets] = None
|
|
326
388
|
|
|
327
389
|
|
|
328
|
-
class
|
|
329
|
-
|
|
390
|
+
class PhoneInfo(BaseModel):
|
|
391
|
+
name: str
|
|
392
|
+
source: str
|
|
393
|
+
href: Optional[str] = None
|
|
394
|
+
carrier: Optional[str] = None
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
T = TypeVar("T")
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class Result(BaseModel, Generic[T]):
|
|
330
401
|
total: int = 0
|
|
331
|
-
hits:
|
|
332
|
-
List[Hit],
|
|
333
|
-
List[TelegramHit],
|
|
334
|
-
List[RansomFeedHit],
|
|
335
|
-
List[TelegramFeedHit],
|
|
336
|
-
List[RansomwareHit],
|
|
337
|
-
List[TelegramProfileHit],
|
|
338
|
-
List[TelegramChannelHit]
|
|
339
|
-
] = Field(default_factory=lambda: [])
|
|
402
|
+
hits: List[T] = Field(default_factory=lambda: [])
|
|
340
403
|
|
|
341
404
|
def __init__(self, **kwargs):
|
|
342
405
|
super().__init__(**kwargs)
|
|
@@ -349,24 +412,43 @@ class Result(BaseModel):
|
|
|
349
412
|
|
|
350
413
|
return type(self.hits[0])
|
|
351
414
|
|
|
415
|
+
@model_validator(mode="before")
|
|
416
|
+
@classmethod
|
|
417
|
+
def check_hits(cls, data):
|
|
418
|
+
hits = data.get("hits")
|
|
419
|
+
if not isinstance(hits, list):
|
|
420
|
+
raise ValueError("hits must be a list")
|
|
421
|
+
|
|
422
|
+
return data
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class ErrorCode(int, Enum):
|
|
426
|
+
BAD_REQUEST = 400
|
|
427
|
+
UNAUTHORIZED = 401
|
|
428
|
+
FORBIDDEN = 403
|
|
429
|
+
NOT_FOUND = 404
|
|
430
|
+
CONFLICT = 409
|
|
431
|
+
UNPROCESSABLE_ENTITY = 422
|
|
432
|
+
TOO_MANY_REQUESTS = 429
|
|
433
|
+
INTERNAL_SERVER_ERROR = 500
|
|
434
|
+
|
|
352
435
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
""
|
|
356
|
-
|
|
357
|
-
""
|
|
436
|
+
class ErrorMessage(str, Enum):
|
|
437
|
+
BAD_REQUEST = "Bad Request"
|
|
438
|
+
UNAUTHORIZED = "Unauthorized"
|
|
439
|
+
FORBIDDEN = "Forbidden"
|
|
440
|
+
NOT_FOUND = "Not Found"
|
|
441
|
+
CONFLICT = "Conflict"
|
|
442
|
+
UNPROCESSABLE_ENTITY = "Unprocessable Entity"
|
|
443
|
+
TOO_MANY_REQUESTS = "Too Many Requests"
|
|
444
|
+
INTERNAL_SERVER_ERROR = "Internal Server Error"
|
|
358
445
|
|
|
359
|
-
data: Result # TODO Add type to all JSON:API responses
|
|
360
446
|
|
|
447
|
+
class Error(BaseModel):
|
|
448
|
+
code: ErrorCode
|
|
449
|
+
message: str
|
|
361
450
|
|
|
362
|
-
class VysionError(BaseModel):
|
|
363
|
-
class StatusCode(int, Enum):
|
|
364
|
-
UNK = 000
|
|
365
|
-
OK = 200
|
|
366
|
-
REQ_ERROR = 400
|
|
367
|
-
UNAUTHORIZED = 403
|
|
368
|
-
NOT_FOUND = 404
|
|
369
|
-
INTERNAL_ERROR = 500
|
|
370
451
|
|
|
371
|
-
|
|
372
|
-
|
|
452
|
+
class VysionResponse(BaseModel, Generic[T]):
|
|
453
|
+
data: Optional[Result[T]] = None
|
|
454
|
+
error: Optional[Error] = None
|
vysion/dto/util.py
CHANGED
|
@@ -14,9 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
14
14
|
See the License for the specific language governing permissions and
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
|
-
import json
|
|
18
17
|
|
|
19
|
-
from pymisp import MISPAttribute, MISPEvent, MISPObject
|
|
18
|
+
from pymisp import MISPAttribute, MISPEvent, MISPObject
|
|
20
19
|
|
|
21
20
|
try:
|
|
22
21
|
from types import NoneType
|
|
@@ -24,15 +23,14 @@ except:
|
|
|
24
23
|
NoneType = type(None)
|
|
25
24
|
|
|
26
25
|
import vysion.dto as dto
|
|
27
|
-
from vysion.dto import URL,
|
|
26
|
+
from vysion.dto import URL, DocumentHit, Page, RansomFeedHit
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
class MISPProcessor:
|
|
31
30
|
def __init__(self):
|
|
32
31
|
self.misp_event = MISPEvent()
|
|
33
32
|
|
|
34
|
-
def parse_hit(self, hit:
|
|
35
|
-
|
|
33
|
+
def parse_hit(self, hit: DocumentHit, ref_attribute: MISPAttribute = None, **_):
|
|
36
34
|
page: Page = hit.page
|
|
37
35
|
|
|
38
36
|
# TODO Add more page parameters
|
|
@@ -48,8 +46,8 @@ class MISPProcessor:
|
|
|
48
46
|
network = url.network
|
|
49
47
|
misp_object.add_attribute("network", type="text", value=network)
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
misp_object.add_attribute("title", type="text", value=
|
|
49
|
+
pageTitle = page.pageTitle
|
|
50
|
+
misp_object.add_attribute("title", type="text", value=pageTitle)
|
|
53
51
|
|
|
54
52
|
url_vysion: URL = "https://app.vysion.ai/document/" + page.id
|
|
55
53
|
misp_object.add_attribute("url_vysion", type="url", value=url_vysion)
|
|
@@ -64,7 +62,7 @@ class MISPProcessor:
|
|
|
64
62
|
# TODO Remove this addition when the vysion-page object works
|
|
65
63
|
self.misp_event.add_attribute("url", value=url.build())
|
|
66
64
|
|
|
67
|
-
self.misp_event.add_attribute("domain", value=url.
|
|
65
|
+
self.misp_event.add_attribute("domain", value=url.domainName)
|
|
68
66
|
|
|
69
67
|
for email in hit.email:
|
|
70
68
|
self.misp_event.add_attribute("email", value=email.value)
|
|
@@ -91,31 +89,34 @@ class MISPProcessor:
|
|
|
91
89
|
self.misp_event.add_tag(str(tag))
|
|
92
90
|
|
|
93
91
|
def parse_ransom_feed_hit(self, hit: RansomFeedHit, **kwargs):
|
|
94
|
-
|
|
95
92
|
# TODO Add event info!
|
|
96
93
|
|
|
97
94
|
misp_object = MISPObject("vysion-ransomware-feed")
|
|
98
95
|
misp_object.template_uuid = "e0bfa994-c184-4894-bfaa-73b1350746e1"
|
|
99
|
-
misp_object[
|
|
100
|
-
"
|
|
101
|
-
|
|
96
|
+
misp_object["meta-category"] = (
|
|
97
|
+
"misc" # TODO Esto se tiene que poder hacer de otra manera... Y sólo es necesario en los feeds
|
|
98
|
+
)
|
|
102
99
|
|
|
103
100
|
misp_object.add_attribute("id", type="text", value=hit.id)
|
|
104
|
-
misp_object.add_attribute("
|
|
105
|
-
misp_object.add_attribute("
|
|
106
|
-
misp_object.add_attribute("
|
|
107
|
-
misp_object.add_attribute("
|
|
108
|
-
misp_object.add_attribute("
|
|
109
|
-
misp_object.add_attribute("
|
|
101
|
+
misp_object.add_attribute("companyName", type="target-org", value=hit.companyName)
|
|
102
|
+
misp_object.add_attribute("companyLink", type="link", value=hit.companyLink)
|
|
103
|
+
misp_object.add_attribute("url", type="link", value=hit.url)
|
|
104
|
+
misp_object.add_attribute("ransomwareGroup", type="threat-actor", value=hit.ransomwareGroup)
|
|
105
|
+
misp_object.add_attribute("detectionDate", type="datetime", value=hit.detectionDate)
|
|
106
|
+
misp_object.add_attribute("text", type="text", value=hit.text)
|
|
110
107
|
misp_object.add_attribute("country", type="text", value=hit.country)
|
|
111
|
-
misp_object.add_attribute(
|
|
108
|
+
misp_object.add_attribute(
|
|
109
|
+
"url_vysion_ransom",
|
|
110
|
+
type="link",
|
|
111
|
+
value="https://app.vysion.ai/victim/" + hit.id,
|
|
112
|
+
)
|
|
112
113
|
|
|
113
114
|
self.misp_event.add_object(misp_object)
|
|
114
115
|
|
|
115
116
|
def process(self, result: dto.Result, **kwargs) -> MISPEvent:
|
|
116
|
-
|
|
117
|
+
|
|
117
118
|
processor = {
|
|
118
|
-
|
|
119
|
+
DocumentHit: self.parse_hit,
|
|
119
120
|
RansomFeedHit: self.parse_ransom_feed_hit,
|
|
120
121
|
}.get(result.get_type(), lambda *_, **__: {})
|
|
121
122
|
|
vysion/model/__init__.py
CHANGED
vysion/model/enum/languages.py
CHANGED
vysion/model/enum/networks.py
CHANGED
vysion/model/enum/services.py
CHANGED
vysion/taxonomy/taxonomy.py
CHANGED
|
@@ -14,13 +14,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
14
14
|
See the License for the specific language governing permissions and
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
|
-
from typing import Dict
|
|
18
17
|
|
|
19
18
|
from vysion.taxonomy.flavours import (
|
|
20
19
|
MISP,
|
|
21
20
|
DBSafe,
|
|
22
21
|
EmptyFlavour,
|
|
23
|
-
Flavour,
|
|
24
22
|
Flavours,
|
|
25
23
|
Vysion,
|
|
26
24
|
)
|
vysion/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vysion
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: The official Python client library for Vysion
|
|
5
5
|
Home-page: https://vysion.ai
|
|
6
6
|
License: Apache-2.0
|
|
@@ -29,7 +29,7 @@ Welcome to the PyPi webpage for Vysion, our implementation as a Python library t
|
|
|
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: [
|
|
32
|
+
Latest version: [2.0.1](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
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
vysion/__init__.py,sha256=yxJiM-S29q8GB3PBff9tV70P2nvydd8aKLEprLkXw5o,664
|
|
2
|
+
vysion/client/__init__.py,sha256=aVHmBuetPdybp7TgcNzUx4HkxTjEuXYzSdDYiYfFQd0,630
|
|
3
|
+
vysion/client/client.py,sha256=RjsPv5Zrr6CBEC2tWB7xDk7tuF0WAssrhCYp_hXFCd8,11178
|
|
4
|
+
vysion/client/error.py,sha256=-3NGr9ngTGSy-ctqk6S9eMBAvtY3lnjv9i0KiuRjd5g,1158
|
|
5
|
+
vysion/dto/__init__.py,sha256=ct8JxVMfJ0APiOTgr9ju-JIuBlXOrPkx7n2qISSXUts,605
|
|
6
|
+
vysion/dto/dto.py,sha256=H7uQMwcbFsVh2TTESdCooA-l6cP8XQ8iS9hrNC2F-UQ,13054
|
|
7
|
+
vysion/dto/tag.py,sha256=_Dn4-_xiC1PD4udp3m4FsDdyHVCSgrq7NFJomjsMYtU,1503
|
|
8
|
+
vysion/dto/util.py,sha256=UNpy_A7rsfH57xgEmDultCIOtyM0a9OzI1PpWUSJi_U,4571
|
|
9
|
+
vysion/model/__init__.py,sha256=rJX9eNW9-jQGTgwm97W04jhZa-dV_dSTTTFcbWg73w0,606
|
|
10
|
+
vysion/model/enum/__init__.py,sha256=lhG6rgaYjrFBR8_IfJL0OoT0L7vooR5_ht4zUahQcOc,690
|
|
11
|
+
vysion/model/enum/languages.py,sha256=yrYDwLEM2lK5iqdLR16n8xeCeSmsCsIj4sTiuNz33FM,4204
|
|
12
|
+
vysion/model/enum/networks.py,sha256=t7aVmLeHqfZ5gQwJPLeS68DSDBl6IEnG21h6kOjvCaw,788
|
|
13
|
+
vysion/model/enum/ransom_groups.py,sha256=_-DFvxUUSxb6r4Xy1Cgpoib2S2qWQmAok-BH-1uEVo0,1526
|
|
14
|
+
vysion/model/enum/services.py,sha256=fUfZEO0RNNctU4Gzae5TV3c7BU5A_cEwaEb1tzqgcr0,5568
|
|
15
|
+
vysion/taxonomy/__init__.py,sha256=Bc364AYxRCyjIn26-XBPeEDCPe3Hma4r5RAI8R8eblU,635
|
|
16
|
+
vysion/taxonomy/flavours.py,sha256=ubImHBE8v0zhzFy_2YozAQk2SzN1XDQwHAxb2RebtCo,2347
|
|
17
|
+
vysion/taxonomy/taxonomy.py,sha256=mG6vHX5Eyp38AKyJBgfBDqdpEmDMbkv8TA8YUBzko54,12227
|
|
18
|
+
vysion/version.py,sha256=UZ0L2QPjFezLeUXGLXw32JOqE2BwkSNth2rnLnRXYlw,610
|
|
19
|
+
vysion-2.0.1.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
20
|
+
vysion-2.0.1.dist-info/METADATA,sha256=vlt_td133OtsJuwEo6_58i3alvbhGlPU0Wc1jBE_6Bo,2122
|
|
21
|
+
vysion-2.0.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
22
|
+
vysion-2.0.1.dist-info/RECORD,,
|