pylookyloo 1.23.1__py3-none-any.whl → 1.24.0__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.
pylookyloo/api.py CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import annotations
3
4
 
4
5
  import base64
5
6
  import warnings
6
7
 
7
8
  from importlib.metadata import version
8
9
  from io import BytesIO, StringIO
9
- from typing import Optional, Dict, Any, List, Union, TypedDict, overload
10
+ from typing import Any, TypedDict, overload
10
11
  from urllib.parse import urljoin, urlparse
11
12
  from pathlib import PurePosixPath, Path
12
13
 
@@ -24,39 +25,39 @@ class AuthError(PyLookylooError):
24
25
  class CaptureSettings(TypedDict, total=False):
25
26
  '''The capture settings that can be passed to Lookyloo.'''
26
27
 
27
- url: Optional[str]
28
- document_name: Optional[str]
29
- document: Optional[str]
30
- browser: Optional[str]
31
- device_name: Optional[str]
32
- user_agent: Optional[str]
33
- proxy: Optional[Union[str, Dict[str, str]]]
34
- general_timeout_in_sec: Optional[int]
35
- cookies: Optional[List[Dict[str, Any]]]
36
- headers: Optional[Union[str, Dict[str, str]]]
37
- http_credentials: Optional[Dict[str, int]]
38
- geolocation: Optional[Dict[str, float]]
39
- timezone_id: Optional[str]
40
- locale: Optional[str]
41
- color_scheme: Optional[str]
42
- viewport: Optional[Dict[str, int]]
43
- referer: Optional[str]
44
-
45
- listing: Optional[bool]
46
- auto_report: Optional[Union[bool, Dict[str, str]]]
28
+ url: str | None
29
+ document_name: str | None
30
+ document: str | None
31
+ browser: str | None
32
+ device_name: str | None
33
+ user_agent: str | None
34
+ proxy: str | dict[str, str] | None
35
+ general_timeout_in_sec: int | None
36
+ cookies: list[dict[str, Any]] | None
37
+ headers: str | dict[str, str] | None
38
+ http_credentials: dict[str, int] | None
39
+ geolocation: dict[str, float] | None
40
+ timezone_id: str | None
41
+ locale: str | None
42
+ color_scheme: str | None
43
+ viewport: dict[str, int] | None
44
+ referer: str | None
45
+
46
+ listing: bool | None
47
+ auto_report: bool | dict[str, str] | None
47
48
 
48
49
 
49
50
  class CompareSettings(TypedDict, total=False):
50
51
  '''The settings that can be passed to the compare method on lookyloo side to filter out some differences'''
51
52
 
52
- ressources_ignore_domains: Optional[List[str]]
53
- ressources_ignore_regexes: Optional[List[str]]
53
+ ressources_ignore_domains: list[str] | None
54
+ ressources_ignore_regexes: list[str] | None
54
55
 
55
56
 
56
57
  class Lookyloo():
57
58
 
58
- def __init__(self, root_url: str='https://lookyloo.circl.lu/', useragent: Optional[str]=None,
59
- *, proxies: Optional[Dict[str, str]]=None):
59
+ def __init__(self, root_url: str='https://lookyloo.circl.lu/', useragent: str | None=None,
60
+ *, proxies: dict[str, str] | None=None):
60
61
  '''Query a specific lookyloo instance.
61
62
 
62
63
  :param root_url: URL of the instance to query.
@@ -73,7 +74,7 @@ class Lookyloo():
73
74
  self.session.headers['user-agent'] = useragent if useragent else f'PyLookyloo / {version("pylookyloo")}'
74
75
  if proxies:
75
76
  self.session.proxies.update(proxies)
76
- self.apikey: Optional[str] = None
77
+ self.apikey: str | None = None
77
78
 
78
79
  @property
79
80
  def is_up(self) -> bool:
@@ -84,7 +85,7 @@ class Lookyloo():
84
85
  return False
85
86
  return r.status_code == 200
86
87
 
87
- def get_status(self, tree_uuid: str) -> Dict[str, Any]:
88
+ def get_status(self, tree_uuid: str) -> dict[str, Any]:
88
89
  '''Get the status of a capture:
89
90
  * -1: Unknown capture.
90
91
  * 0: The capture is queued up but not processed yet.
@@ -94,24 +95,24 @@ class Lookyloo():
94
95
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'status'))))
95
96
  return r.json()
96
97
 
97
- def get_capture_stats(self, tree_uuid: str) -> Dict[str, Any]:
98
+ def get_capture_stats(self, tree_uuid: str) -> dict[str, Any]:
98
99
  '''Get statistics of the capture'''
99
100
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'stats'))))
100
101
  return r.json()
101
102
 
102
- def get_info(self, tree_uuid: str) -> Dict[str, Any]:
103
+ def get_info(self, tree_uuid: str) -> dict[str, Any]:
103
104
  '''Get information about the capture (url, timestamp, user agent)'''
104
105
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'info'))))
105
106
  return r.json()
106
107
 
107
- def get_comparables(self, tree_uuid: str) -> Dict[str, Any]:
108
+ def get_comparables(self, tree_uuid: str) -> dict[str, Any]:
108
109
  '''Get comparable information from the capture'''
109
110
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'comparables'))))
110
111
  return r.json()
111
112
 
112
- def enqueue(self, url: Optional[str]=None, quiet: bool=False, # type: ignore[no-untyped-def]
113
- document: Optional[Union[Path, BytesIO]]=None,
114
- document_name: Optional[str]=None, **kwargs) -> str:
113
+ def enqueue(self, url: str | None=None, quiet: bool=False, # type: ignore[no-untyped-def]
114
+ document: Path | BytesIO | None=None,
115
+ document_name: str | None=None, **kwargs) -> str:
115
116
  '''Enqueue an URL.
116
117
 
117
118
  :param url: URL to enqueue
@@ -126,50 +127,50 @@ class Lookyloo():
126
127
 
127
128
  @overload
128
129
  def submit(self, *, quiet: bool=False,
129
- capture_settings: Optional[CaptureSettings]=None) -> str:
130
+ capture_settings: CaptureSettings | None=None) -> str:
130
131
  ...
131
132
 
132
133
  @overload
133
134
  def submit(self, *, quiet: bool=False,
134
- url: Optional[str]=None,
135
- document_name: Optional[str]=None, document: Optional[Union[Path, BytesIO]]=None,
136
- browser: Optional[str]=None, device_name: Optional[str]=None,
137
- user_agent: Optional[str]=None,
138
- proxy: Optional[Union[str, Dict[str, str]]]=None,
139
- general_timeout_in_sec: Optional[int]=None,
140
- cookies: Optional[List[Dict[str, Any]]]=None,
141
- headers: Optional[Union[str, Dict[str, str]]]=None,
142
- http_credentials: Optional[Dict[str, int]]=None,
143
- geolocation: Optional[Dict[str, float]]=None,
144
- timezone_id: Optional[str]=None,
145
- locale: Optional[str]=None,
146
- color_scheme: Optional[str]=None,
147
- viewport: Optional[Dict[str, int]]=None,
148
- referer: Optional[str]=None,
149
- listing: Optional[bool]=None,
150
- auto_report: Optional[Union[bool, Dict[str, str]]]=None
135
+ url: str | None=None,
136
+ document_name: str | None=None, document: Path | BytesIO | None=None,
137
+ browser: str | None=None, device_name: str | None=None,
138
+ user_agent: str | None=None,
139
+ proxy: str | dict[str, str] | None=None,
140
+ general_timeout_in_sec: int | None=None,
141
+ cookies: list[dict[str, Any]] | None=None,
142
+ headers: str | dict[str, str] | None=None,
143
+ http_credentials: dict[str, int] | None=None,
144
+ geolocation: dict[str, float] | None=None,
145
+ timezone_id: str | None=None,
146
+ locale: str | None=None,
147
+ color_scheme: str | None=None,
148
+ viewport: dict[str, int] | None=None,
149
+ referer: str | None=None,
150
+ listing: bool | None=None,
151
+ auto_report: bool | dict[str, str] | None=None
151
152
  ) -> str:
152
153
  ...
153
154
 
154
155
  def submit(self, *, quiet: bool=False,
155
- capture_settings: Optional[CaptureSettings]=None,
156
- url: Optional[str]=None,
157
- document_name: Optional[str]=None, document: Optional[Union[Path, BytesIO]]=None,
158
- browser: Optional[str]=None, device_name: Optional[str]=None,
159
- user_agent: Optional[str]=None,
160
- proxy: Optional[Union[str, Dict[str, str]]]=None,
161
- general_timeout_in_sec: Optional[int]=None,
162
- cookies: Optional[List[Dict[str, Any]]]=None,
163
- headers: Optional[Union[str, Dict[str, str]]]=None,
164
- http_credentials: Optional[Dict[str, int]]=None,
165
- geolocation: Optional[Dict[str, float]]=None,
166
- timezone_id: Optional[str]=None,
167
- locale: Optional[str]=None,
168
- color_scheme: Optional[str]=None,
169
- viewport: Optional[Dict[str, int]]=None,
170
- referer: Optional[str]=None,
171
- listing: Optional[bool]=None,
172
- auto_report: Optional[Union[bool, Dict[str, str]]]=None
156
+ capture_settings: CaptureSettings | None=None,
157
+ url: str | None=None,
158
+ document_name: str | None=None, document: Path | BytesIO | None=None,
159
+ browser: str | None=None, device_name: str | None=None,
160
+ user_agent: str | None=None,
161
+ proxy: str | dict[str, str] | None=None,
162
+ general_timeout_in_sec: int | None=None,
163
+ cookies: list[dict[str, Any]] | None=None,
164
+ headers: str | dict[str, str] | None=None,
165
+ http_credentials: dict[str, int] | None=None,
166
+ geolocation: dict[str, float] | None=None,
167
+ timezone_id: str | None=None,
168
+ locale: str | None=None,
169
+ color_scheme: str | None=None,
170
+ viewport: dict[str, int] | None=None,
171
+ referer: str | None=None,
172
+ listing: bool | None=None,
173
+ auto_report: bool | dict[str, str] | None=None
173
174
  ) -> str:
174
175
  '''Submit a URL to a lookyloo instance.
175
176
 
@@ -261,13 +262,13 @@ class Lookyloo():
261
262
  return uuid
262
263
  return urljoin(self.root_url, f'tree/{uuid}')
263
264
 
264
- def get_apikey(self, username: str, password: str) -> Dict[str, str]:
265
+ def get_apikey(self, username: str, password: str) -> dict[str, str]:
265
266
  '''Get the API key for the given user.'''
266
267
  to_post = {'username': username, 'password': password}
267
268
  r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'get_token'))), json=to_post)
268
269
  return r.json()
269
270
 
270
- def init_apikey(self, username: Optional[str]=None, password: Optional[str]=None, apikey: Optional[str]=None) -> None:
271
+ def init_apikey(self, username: str | None=None, password: str | None=None, apikey: str | None=None) -> None:
271
272
  '''Init the API key for the current session. All the requests against lookyloo after this call will be authenticated.'''
272
273
  if apikey:
273
274
  self.apikey = apikey
@@ -282,12 +283,12 @@ class Lookyloo():
282
283
  else:
283
284
  raise AuthError('Unable to initialize API key')
284
285
 
285
- def misp_export(self, tree_uuid: str) -> Dict[str, Any]:
286
+ def misp_export(self, tree_uuid: str) -> dict[str, Any]:
286
287
  '''Export the capture in MISP format'''
287
288
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'misp_export'))))
288
289
  return r.json()
289
290
 
290
- def misp_push(self, tree_uuid: str) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
291
+ def misp_push(self, tree_uuid: str) -> dict[str, Any] | list[dict[str, Any]]:
291
292
  '''Push the capture to a pre-configured MISP instance (requires an authenticated user, use init_apikey first)
292
293
  Note: if the response is a dict, it is an error mesage. If it is a list, it's a list of MISP event.
293
294
  '''
@@ -296,7 +297,7 @@ class Lookyloo():
296
297
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'misp_push'))))
297
298
  return r.json()
298
299
 
299
- def trigger_modules(self, tree_uuid: str, force: bool=False) -> Dict[str, Any]:
300
+ def trigger_modules(self, tree_uuid: str, force: bool=False) -> dict[str, Any]:
300
301
  '''Trigger all the available 3rd party modules on the given capture.
301
302
  :param force: Trigger the modules even if they were already triggered today.
302
303
  '''
@@ -305,21 +306,21 @@ class Lookyloo():
305
306
  json=to_send)
306
307
  return r.json()
307
308
 
308
- def rebuild_capture(self, tree_uuid: str) -> Dict[str, str]:
309
+ def rebuild_capture(self, tree_uuid: str) -> dict[str, str]:
309
310
  '''Force rebuild a capture (requires an authenticated user, use init_apikey first)'''
310
311
  if not self.apikey:
311
312
  raise AuthError('You need to initialize the apikey to use this method (see init_apikey)')
312
- r = self.session.get(urljoin(self.root_url, str(PurePosixPath('admin', tree_uuid, 'rebuild'))))
313
+ r = self.session.post(urljoin(self.root_url, str(PurePosixPath('admin', tree_uuid, 'rebuild'))))
313
314
  return r.json()
314
315
 
315
- def hide_capture(self, tree_uuid: str) -> Dict[str, str]:
316
+ def hide_capture(self, tree_uuid: str) -> dict[str, str]:
316
317
  '''Hide a capture from the index page (requires an authenticated user, use init_apikey first)'''
317
318
  if not self.apikey:
318
319
  raise AuthError('You need to initialize the apikey to use this method (see init_apikey)')
319
- r = self.session.get(urljoin(self.root_url, str(PurePosixPath('admin', tree_uuid, 'hide'))))
320
+ r = self.session.post(urljoin(self.root_url, str(PurePosixPath('admin', tree_uuid, 'hide'))))
320
321
  return r.json()
321
322
 
322
- def get_redirects(self, capture_uuid: str) -> Dict[str, Any]:
323
+ def get_redirects(self, capture_uuid: str) -> dict[str, Any]:
323
324
  '''Returns the initial redirects.
324
325
 
325
326
  :param capture_uuid: UUID of the capture
@@ -327,7 +328,7 @@ class Lookyloo():
327
328
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', capture_uuid, 'redirects'))))
328
329
  return r.json()
329
330
 
330
- def get_urls(self, capture_uuid: str) -> Dict[str, Any]:
331
+ def get_urls(self, capture_uuid: str) -> dict[str, Any]:
331
332
  '''Returns all the URLs seen during the capture.
332
333
 
333
334
  :param capture_uuid: UUID of the capture
@@ -335,7 +336,7 @@ class Lookyloo():
335
336
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', capture_uuid, 'urls'))))
336
337
  return r.json()
337
338
 
338
- def get_hostnames(self, capture_uuid: str) -> Dict[str, Any]:
339
+ def get_hostnames(self, capture_uuid: str) -> dict[str, Any]:
339
340
  '''Returns all the hostnames seen during the capture.
340
341
 
341
342
  :param capture_uuid: UUID of the capture
@@ -359,7 +360,7 @@ class Lookyloo():
359
360
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('bin', capture_uuid, 'data'))))
360
361
  return BytesIO(r.content)
361
362
 
362
- def get_cookies(self, capture_uuid: str) -> List[Dict[str, str]]:
363
+ def get_cookies(self, capture_uuid: str) -> list[dict[str, str]]:
363
364
  '''Returns the complete cookies jar.
364
365
 
365
366
  :param capture_uuid: UUID of the capture
@@ -382,7 +383,7 @@ class Lookyloo():
382
383
  :param algorithm: The algorithm of the hashes
383
384
  :param hashes_only: If False, will also return the URLs related to the hashes
384
385
  '''
385
- params: Dict[str, Union[str, int]] = {'algorithm': algorithm, 'hashes_only': int(hashes_only)}
386
+ params: dict[str, str | int] = {'algorithm': algorithm, 'hashes_only': int(hashes_only)}
386
387
 
387
388
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', capture_uuid, 'hashes'))), params=params)
388
389
  return r.json()
@@ -395,7 +396,7 @@ class Lookyloo():
395
396
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('bin', capture_uuid, 'export'))))
396
397
  return BytesIO(r.content)
397
398
 
398
- def get_hash_occurrences(self, h: str) -> Dict[str, Any]:
399
+ def get_hash_occurrences(self, h: str) -> dict[str, Any]:
399
400
  '''Returns the base 64 body related the the hash, and a list of all the captures containing that hash.
400
401
 
401
402
  :param h: sha512 to search
@@ -403,7 +404,7 @@ class Lookyloo():
403
404
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', 'hash_info', h))))
404
405
  return r.json()
405
406
 
406
- def get_url_occurrences(self, url: str, limit: int=20, cached_captures_only: bool=True) -> Dict[str, Any]:
407
+ def get_url_occurrences(self, url: str, limit: int=20, cached_captures_only: bool=True) -> dict[str, Any]:
407
408
  '''Returns all the captures contining the URL
408
409
 
409
410
  :param url: URL to lookup
@@ -414,7 +415,7 @@ class Lookyloo():
414
415
  json={'url': url, 'limit': limit})
415
416
  return r.json()
416
417
 
417
- def get_hostname_occurrences(self, hostname: str, with_urls_occurrences: bool=False, limit: int=20, cached_captures_only: bool=True) -> Dict[str, Any]:
418
+ def get_hostname_occurrences(self, hostname: str, with_urls_occurrences: bool=False, limit: int=20, cached_captures_only: bool=True) -> dict[str, Any]:
418
419
  '''Returns all the captures contining the hostname. It will be pretty slow on very common domains.
419
420
 
420
421
  :param hostname: Hostname to lookup
@@ -426,13 +427,13 @@ class Lookyloo():
426
427
  json={'hostname': hostname, 'with_urls_occurrences': with_urls_occurrences, 'limit': limit})
427
428
  return r.json()
428
429
 
429
- def get_stats(self) -> Dict[str, Any]:
430
+ def get_stats(self) -> dict[str, Any]:
430
431
  '''Returns all the captures contining the URL'''
431
432
 
432
433
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', 'stats'))))
433
434
  return r.json()
434
435
 
435
- def get_takedown_information(self, capture_uuid: str) -> Dict[str, Any]:
436
+ def get_takedown_information(self, capture_uuid: str) -> dict[str, Any]:
436
437
  '''Returns information required to request a takedown for a capture
437
438
 
438
439
  :param capture_uuid: UUID of the capture
@@ -441,7 +442,7 @@ class Lookyloo():
441
442
  json={'capture_uuid': capture_uuid})
442
443
  return r.json()
443
444
 
444
- def compare_captures(self, capture_left: str, capture_right: str, /, *, compare_settings: Optional[CompareSettings]=None) -> Dict[str, Any]:
445
+ def compare_captures(self, capture_left: str, capture_right: str, /, *, compare_settings: CompareSettings | None=None) -> dict[str, Any]:
445
446
  '''Compares two captures
446
447
 
447
448
  :param capture_left: UUID of the capture to compare from
@@ -453,3 +454,18 @@ class Lookyloo():
453
454
  'capture_right': capture_right,
454
455
  'compare_settings': compare_settings})
455
456
  return r.json()
457
+
458
+ def get_modules_responses(self, tree_uuid: str) -> dict[str, Any]:
459
+ '''Returns information from the 3rd party modules
460
+
461
+ :param capture_uuid: UUID of the capture
462
+ '''
463
+ r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'modules'))))
464
+ return r.json()
465
+
466
+ def send_mail(self, tree_uuid: str, email: str = '', comment: str | None = None) -> bool | dict[str, Any]:
467
+ to_send = {'email': email}
468
+ if comment:
469
+ to_send['comment'] = comment
470
+ r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'report'))), json=to_send)
471
+ return r.json()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pylookyloo
3
- Version: 1.23.1
3
+ Version: 1.24.0
4
4
  Summary: Python CLI and module for Lookyloo
5
5
  Home-page: https://github.com/lookyloo/PyLookyloo
6
6
  License: BSD-3-Clause
@@ -0,0 +1,8 @@
1
+ pylookyloo/__init__.py,sha256=_JYXwXHL7ShZkeruvGd8qDTpxNRfuDjvV65SOMMU6yc,1922
2
+ pylookyloo/api.py,sha256=nbuCJmeGy34ZCFH5kZeVwJlysgmn_h6ZiQrqYAmA5iM,21748
3
+ pylookyloo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ pylookyloo-1.24.0.dist-info/LICENSE,sha256=4C4hLYrIkUD96Ggk-y_Go1Qf7PBZrEm9PSeTGe2nd4s,1516
5
+ pylookyloo-1.24.0.dist-info/METADATA,sha256=GV8FRY1VU92q8jN8-Ko5Tohul-Vf0J0a7AM_S0Vsrvw,2340
6
+ pylookyloo-1.24.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
7
+ pylookyloo-1.24.0.dist-info/entry_points.txt,sha256=y2c0Ujg8co6Xyf7MoxStVU-fLQMZBSGAg-KFidmsha4,44
8
+ pylookyloo-1.24.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.8.1
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,8 +0,0 @@
1
- pylookyloo/__init__.py,sha256=_JYXwXHL7ShZkeruvGd8qDTpxNRfuDjvV65SOMMU6yc,1922
2
- pylookyloo/api.py,sha256=pUNC0DoaxYlvhOkDKHF4bSqRwABiCv1UkZgPz8zRIBM,21392
3
- pylookyloo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- pylookyloo-1.23.1.dist-info/LICENSE,sha256=4C4hLYrIkUD96Ggk-y_Go1Qf7PBZrEm9PSeTGe2nd4s,1516
5
- pylookyloo-1.23.1.dist-info/METADATA,sha256=qltwCMFtvxCE5IO22c5uEaAukdc1l0h_hSEFVdoRI6Q,2340
6
- pylookyloo-1.23.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
7
- pylookyloo-1.23.1.dist-info/entry_points.txt,sha256=y2c0Ujg8co6Xyf7MoxStVU-fLQMZBSGAg-KFidmsha4,44
8
- pylookyloo-1.23.1.dist-info/RECORD,,