pylookyloo 1.30.0__tar.gz → 1.31.1__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.

Potentially problematic release.


This version of pylookyloo might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pylookyloo
3
- Version: 1.30.0
3
+ Version: 1.31.1
4
4
  Summary: Python CLI and module for Lookyloo
5
5
  License: GPL-2.0-or-later
6
6
  Author: Raphaël Vinot
@@ -24,8 +24,8 @@ Classifier: Topic :: Security
24
24
  Provides-Extra: docs
25
25
  Provides-Extra: examples
26
26
  Requires-Dist: Sphinx (>=8.2.3) ; (python_version >= "3.11") and (extra == "docs")
27
- Requires-Dist: pylacus (>=1.15.0) ; extra == "examples"
28
- Requires-Dist: requests (>=2.32.3)
27
+ Requires-Dist: pylacus (>=1.15.1) ; extra == "examples"
28
+ Requires-Dist: requests (>=2.32.4)
29
29
  Project-URL: Documentation, https://pylookyloo.readthedocs.io/en/latest/
30
30
  Project-URL: Repository, https://github.com/lookyloo/PyLookyloo
31
31
  Project-URL: issues, https://github.com/lookyloo/PyLookyloo/issues
@@ -7,6 +7,7 @@ import base64
7
7
  import warnings
8
8
 
9
9
  from datetime import datetime
10
+ from hashlib import sha512
10
11
  from importlib.metadata import version
11
12
  from io import BytesIO, StringIO
12
13
  from typing import Any, TypedDict, overload, Literal
@@ -89,7 +90,7 @@ class CompareSettings(TypedDict, total=False):
89
90
  class Lookyloo():
90
91
 
91
92
  def __init__(self, root_url: str='https://lookyloo.circl.lu/', useragent: str | None=None,
92
- *, proxies: dict[str, str] | None=None):
93
+ *, proxies: dict[str, str] | None=None, verify: bool | str=True) -> None:
93
94
  '''Query a specific lookyloo instance.
94
95
 
95
96
  :param root_url: URL of the instance to query.
@@ -109,6 +110,7 @@ class Lookyloo():
109
110
  self.session.proxies.update(proxies)
110
111
  retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504])
111
112
  self.session.mount('http://', HTTPAdapter(max_retries=retries))
113
+ self.session.verify = verify
112
114
 
113
115
  @property
114
116
  def is_up(self) -> bool:
@@ -391,6 +393,14 @@ class Lookyloo():
391
393
  r = self.session.post(urljoin(self.root_url, str(PurePosixPath('admin', tree_uuid, 'remove'))))
392
394
  return r.json()
393
395
 
396
+ def get_favicons(self, capture_uuid: str) -> dict[str, Any]:
397
+ '''Returns the potential favicons of the capture.
398
+
399
+ :param capture_uuid: UUID of the capture
400
+ '''
401
+ r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', capture_uuid, 'favicons'))))
402
+ return r.json()
403
+
394
404
  def get_redirects(self, capture_uuid: str) -> dict[str, Any]:
395
405
  '''Returns the initial redirects.
396
406
 
@@ -415,6 +425,14 @@ class Lookyloo():
415
425
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', capture_uuid, 'hostnames'))))
416
426
  return r.json()
417
427
 
428
+ def get_ips(self, capture_uuid: str) -> dict[str, Any]:
429
+ '''Returns all the IPs seen during the capture.
430
+
431
+ :param capture_uuid: UUID of the capture
432
+ '''
433
+ r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', capture_uuid, 'ips'))))
434
+ return r.json()
435
+
418
436
  def get_screenshot(self, capture_uuid: str) -> BytesIO:
419
437
  '''Returns the screenshot.
420
438
 
@@ -475,35 +493,78 @@ class Lookyloo():
475
493
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('bin', capture_uuid, 'export'))))
476
494
  return BytesIO(r.content)
477
495
 
478
- def get_hash_occurrences(self, h: str) -> dict[str, Any]:
479
- '''Returns the base 64 body related the the hash, and a list of all the captures containing that hash.
496
+ def get_hash_occurrences(self, h: str, *, with_urls_occurrences: bool=False, cached_captures_only: bool=True, limit: int=20, offset: int=0) -> dict[str, Any]:
497
+ '''Returns the base64 body related the the hash, and a list of all the captures containing that hash.
480
498
 
481
499
  :param h: sha512 to search
500
+ :param with_urls_occurrences: If true, add details about the URLs from the URL nodes in the tree.
501
+ :param cached_captures_only: If False, Lookyloo will attempt to re-cache the missing captures. It might take some time.
502
+ :param limit: The max amount of entries to return.
503
+ :param offset: The offset to start from, useful for pagination.
482
504
  '''
483
- r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', 'hash_info', h))))
505
+ r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'hash_info'))),
506
+ json={'body_hash': h, 'with_urls_occurrences': with_urls_occurrences,
507
+ 'cached_captures_only': cached_captures_only, 'limit': limit, 'offset': offset})
484
508
  return r.json()
485
509
 
486
- def get_url_occurrences(self, url: str, limit: int=20, cached_captures_only: bool=True) -> dict[str, Any]:
510
+ def get_url_occurrences(self, url: str, *, with_urls_occurrences: bool=False, cached_captures_only: bool=True, limit: int=20, offset: int=0) -> dict[str, Any]:
487
511
  '''Returns all the captures contining the URL
488
512
 
489
513
  :param url: URL to lookup
490
- :param limit: The max amount of entries to return.
514
+ :param with_urls_occurrences: If true, add details about the URLs from the URL nodes in the tree.
491
515
  :param cached_captures_only: If False, Lookyloo will attempt to re-cache the missing captures. It might take some time.
516
+ :param limit: The max amount of entries to return.
517
+ :param offset: The offset to start from, useful for pagination.
492
518
  '''
493
519
  r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'url_info'))),
494
- json={'url': url, 'limit': limit})
520
+ json={'url': url, 'with_urls_occurrences': with_urls_occurrences,
521
+ 'cached_captures_only': cached_captures_only,
522
+ 'limit': limit, 'offset': offset})
495
523
  return r.json()
496
524
 
497
- def get_hostname_occurrences(self, hostname: str, with_urls_occurrences: bool=False, limit: int=20, cached_captures_only: bool=True) -> dict[str, Any]:
498
- '''Returns all the captures contining the hostname. It will be pretty slow on very common domains.
525
+ def get_hostname_occurrences(self, hostname: str, *, with_urls_occurrences: bool=False, cached_captures_only: bool=True, limit: int=20, offset: int=0) -> dict[str, Any]:
526
+ '''Returns all the captures contining the hostname.
499
527
 
500
528
  :param hostname: Hostname to lookup
501
529
  :param with_urls_occurrences: If true, add details about the related URLs.
502
- :param limit: The max amount of entries to return.
503
530
  :param cached_captures_only: If False, Lookyloo will attempt to re-cache the missing captures. It might take some time.
531
+ :param limit: The max amount of entries to return.
532
+ :param offset: The offset to start from, useful for pagination.
504
533
  '''
505
534
  r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'hostname_info'))),
506
- json={'hostname': hostname, 'with_urls_occurrences': with_urls_occurrences, 'limit': limit})
535
+ json={'hostname': hostname, 'with_urls_occurrences': with_urls_occurrences,
536
+ 'cached_captures_only': cached_captures_only,
537
+ 'limit': limit, 'offset': offset})
538
+ return r.json()
539
+
540
+ def get_ip_occurrences(self, ip: str, *, with_urls_occurrences: bool=False, cached_captures_only: bool=True, limit: int=20, offset: int=0) -> dict[str, Any]:
541
+ '''Returns all the captures containing the IP address.
542
+
543
+ :param ip: IP to lookup
544
+ :param with_urls_occurrences: If true, add details about the related URLs.
545
+ :param cached_captures_only: If False, Lookyloo will attempt to re-cache the missing captures. It might take some time.
546
+ :param limit: The max amount of entries to return.
547
+ :param offset: The offset to start from, useful for pagination.
548
+ '''
549
+ r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'ip_info'))),
550
+ json={'ip': ip, 'with_urls_occurrences': with_urls_occurrences,
551
+ 'cached_captures_only': cached_captures_only,
552
+ 'limit': limit})
553
+ return r.json()
554
+
555
+ def get_favicon_occurrences(self, favicon: str | BytesIO, *, cached_captures_only: bool=True, limit: int=20, offset: int=0) -> dict[str, Any]:
556
+ '''Returns all the captures containing the favicon.
557
+
558
+ :param favicon: Favicon to lookup. Either the hash, or the file in a BytesIO (hash will be generated on the fly)
559
+ :param cached_captures_only: If False, Lookyloo will attempt to re-cache the missing captures. It might take some time.
560
+ :param limit: The max amount of entries to return.
561
+ :param offset: The offset to start from, useful for pagination.
562
+ '''
563
+ if isinstance(favicon, BytesIO):
564
+ favicon = sha512(favicon.read()).hexdigest()
565
+ r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'favicon_info'))),
566
+ json={'favicon': favicon, 'cached_captures_only': cached_captures_only,
567
+ 'limit': limit, 'offset': offset})
507
568
  return r.json()
508
569
 
509
570
  def get_stats(self) -> dict[str, Any]:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pylookyloo"
3
- version = "1.30.0"
3
+ version = "1.31.1"
4
4
  description = "Python CLI and module for Lookyloo"
5
5
  authors = [
6
6
  {name="Raphaël Vinot", email="raphael.vinot@circl.lu"}
@@ -12,7 +12,7 @@ requires-python = ">=3.9"
12
12
  dynamic = [ "classifiers" ]
13
13
 
14
14
  dependencies = [
15
- "requests (>=2.32.3)"
15
+ "requests (>=2.32.4)"
16
16
  ]
17
17
 
18
18
  [project.urls]
@@ -37,12 +37,12 @@ lookyloo = 'pylookyloo:main'
37
37
 
38
38
  [project.optional-dependencies]
39
39
  docs = ["Sphinx (>=8.2.3) ; python_version >= \"3.11\""]
40
- examples = ["pylacus (>=1.15.0)"]
40
+ examples = ["pylacus (>=1.15.1)"]
41
41
 
42
42
  [tool.poetry.group.dev.dependencies]
43
- mypy = "^1.15.0"
44
- types-requests = "^2.32.0.20250515"
45
- pytest = "^8.3.5"
43
+ mypy = "^1.16.1"
44
+ types-requests = "^2.32.4.20250611"
45
+ pytest = "^8.4.1"
46
46
 
47
47
  [build-system]
48
48
  requires = ["poetry-core>=2.0"]
File without changes
File without changes