pylookyloo 1.24.0__tar.gz → 1.25.0__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.1
2
2
  Name: pylookyloo
3
- Version: 1.24.0
3
+ Version: 1.25.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
@@ -25,7 +25,7 @@ Classifier: Topic :: Security
25
25
  Provides-Extra: docs
26
26
  Requires-Dist: Sphinx (<7.2) ; (python_version < "3.9") and (extra == "docs")
27
27
  Requires-Dist: Sphinx (>=7.2,<8.0) ; (python_version >= "3.9") and (extra == "docs")
28
- Requires-Dist: requests (>=2.31.0,<3.0.0)
28
+ Requires-Dist: requests (>=2.32.3,<3.0.0)
29
29
  Project-URL: Documentation, https://pylookyloo.readthedocs.io/en/latest/
30
30
  Project-URL: Repository, https://github.com/lookyloo/PyLookyloo
31
31
  Description-Content-Type: text/markdown
@@ -2,12 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  import base64
6
7
  import warnings
7
8
 
9
+ from datetime import datetime
8
10
  from importlib.metadata import version
9
11
  from io import BytesIO, StringIO
10
- from typing import Any, TypedDict, overload
12
+ from typing import Any, TypedDict, overload, Literal
11
13
  from urllib.parse import urljoin, urlparse
12
14
  from pathlib import PurePosixPath, Path
13
15
 
@@ -283,6 +285,14 @@ class Lookyloo():
283
285
  else:
284
286
  raise AuthError('Unable to initialize API key')
285
287
 
288
+ def get_user_config(self) -> dict[str, Any] | None:
289
+ '''Get the configuration enforced by the server for the current user (requires an authenticated user, use init_apikey first)
290
+ '''
291
+ if not self.apikey:
292
+ raise AuthError('You need to initialize the apikey to use this method (see init_apikey)')
293
+ r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', 'get_user_config'))))
294
+ return r.json()
295
+
286
296
  def misp_export(self, tree_uuid: str) -> dict[str, Any]:
287
297
  '''Export the capture in MISP format'''
288
298
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'misp_export'))))
@@ -433,13 +443,22 @@ class Lookyloo():
433
443
  r = self.session.get(urljoin(self.root_url, str(PurePosixPath('json', 'stats'))))
434
444
  return r.json()
435
445
 
436
- def get_takedown_information(self, capture_uuid: str) -> dict[str, Any]:
446
+ @overload
447
+ def get_takedown_information(self, capture_uuid: str, filter_contacts: Literal[True]) -> list[str]:
448
+ ...
449
+
450
+ @overload
451
+ def get_takedown_information(self, capture_uuid: str, filter_contacts: Literal[False]=False) -> list[dict[str, Any]]:
452
+ ...
453
+
454
+ def get_takedown_information(self, capture_uuid: str, filter_contacts: bool=False) -> list[dict[str, Any]] | list[str]:
437
455
  '''Returns information required to request a takedown for a capture
438
456
 
439
457
  :param capture_uuid: UUID of the capture
458
+ :param filter_contacts: If True, will only return the contact emails and filter out the invalid ones.
440
459
  '''
441
460
  r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'takedown'))),
442
- json={'capture_uuid': capture_uuid})
461
+ json={'capture_uuid': capture_uuid, 'filter': filter_contacts})
443
462
  return r.json()
444
463
 
445
464
  def compare_captures(self, capture_left: str, capture_right: str, /, *, compare_settings: CompareSettings | None=None) -> dict[str, Any]:
@@ -464,8 +483,109 @@ class Lookyloo():
464
483
  return r.json()
465
484
 
466
485
  def send_mail(self, tree_uuid: str, email: str = '', comment: str | None = None) -> bool | dict[str, Any]:
486
+ '''Reports a capture by sending an email to the investigation team
487
+
488
+ :param tree_uuid: UUID of the capture
489
+ :param email: Email of the reporter, used by the analyst to get in touch
490
+ :param comment: Description of the URL, will be given to the analyst
491
+ '''
467
492
  to_send = {'email': email}
468
493
  if comment:
469
494
  to_send['comment'] = comment
470
495
  r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', tree_uuid, 'report'))), json=to_send)
471
496
  return r.json()
497
+
498
+ def get_recent_captures(self, timestamp: str | datetime | float | None=None) -> list[str]:
499
+ '''Gets the uuids of the most recent captures
500
+
501
+ :param timestamp: Timestamp of the capture
502
+ '''
503
+ if not timestamp:
504
+ url = urljoin(self.root_url, str(PurePosixPath('json', 'recent_captures')))
505
+ else:
506
+ if isinstance(timestamp, datetime):
507
+ timestamp = timestamp.timestamp()
508
+ url = urljoin(self.root_url, str(PurePosixPath('json', 'recent_captures', str(timestamp))))
509
+ r = self.session.get(url)
510
+ return r.json()
511
+
512
+ @overload
513
+ def upload_capture(self, *, quiet: Literal[True],
514
+ listing: bool = False,
515
+ full_capture: Path | BytesIO | str | None = None,
516
+ har: Path | BytesIO | str | None = None,
517
+ html: Path | BytesIO | str | None = None,
518
+ last_redirected_url: str | None = None,
519
+ screenshot: Path | BytesIO | str | None = None) -> str:
520
+ ...
521
+
522
+ @overload
523
+ def upload_capture(self, *, quiet: Literal[False]=False,
524
+ listing: bool = False,
525
+ full_capture: Path | BytesIO | str | None = None,
526
+ har: Path | BytesIO | str | None = None,
527
+ html: Path | BytesIO | str | None = None,
528
+ last_redirected_url: str | None = None,
529
+ screenshot: Path | BytesIO | str | None = None) -> tuple[str, dict[str, str]]:
530
+ ...
531
+
532
+ def upload_capture(self, *, quiet: bool = False,
533
+ listing: bool = False,
534
+ full_capture: Path | BytesIO | str | None = None,
535
+ har: Path | BytesIO | str | None = None,
536
+ html: Path | BytesIO | str | None = None,
537
+ last_redirected_url: str | None = None,
538
+ screenshot: Path | BytesIO | str | None = None) -> str | tuple[str, dict[str, str]]:
539
+ '''Upload a capture via har-file and others
540
+
541
+ :param quiet: Returns the UUID only, instead of the the UUID and the potential error / warning messages
542
+ :param listing: if true the capture should be public, else private - overwritten if the full_capture is given and it contains no_index
543
+ :param full_capture: path to the capture made by another instance
544
+ :param har: Harfile of the capture
545
+ :param html: rendered HTML of the capture
546
+ :param last_redirected_url: The landing page of the capture
547
+ :param screenshot: Screenshot of the capture
548
+ '''
549
+ def encode_document(document: Path | BytesIO | str) -> str:
550
+ if isinstance(document, str):
551
+ if not os.path.exists(document):
552
+ raise FileNotFoundError(f'{document} does not exist')
553
+ document = Path(document)
554
+ if isinstance(document, Path):
555
+ with document.open('rb') as f:
556
+ document = BytesIO(f.read())
557
+ return base64.b64encode(document.getvalue()).decode()
558
+
559
+ to_send: dict[str, Any] = {'listing': listing}
560
+
561
+ if full_capture:
562
+ b64_full_capture = encode_document(full_capture)
563
+ to_send['full_capture'] = b64_full_capture
564
+ elif har:
565
+ b64_har = encode_document(har)
566
+ to_send['har_file'] = b64_har
567
+
568
+ if html:
569
+ b64_html = encode_document(html)
570
+ to_send['html_file'] = b64_html
571
+
572
+ if last_redirected_url:
573
+ to_send['landing_page'] = last_redirected_url
574
+
575
+ if screenshot:
576
+ b64_screenshot = encode_document(screenshot)
577
+ to_send['screenshot_file'] = b64_screenshot
578
+ else:
579
+ raise PyLookylooError("Full capture or at least har-file are required")
580
+
581
+ r = self.session.post(urljoin(self.root_url, str(PurePosixPath('json', 'upload'))), json=to_send)
582
+ r.raise_for_status()
583
+ json_response = r.json()
584
+ uuid = json_response['uuid']
585
+ messages = json_response['messages']
586
+
587
+ if not uuid:
588
+ raise PyLookylooError('Unable to get UUID from lookyloo instance.')
589
+ if quiet:
590
+ return uuid
591
+ return uuid, messages
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pylookyloo"
3
- version = "1.24.0"
3
+ version = "1.25.0"
4
4
  description = "Python CLI and module for Lookyloo"
5
5
  authors = ["Raphaël Vinot <raphael.vinot@circl.lu>"]
6
6
  license = "BSD-3-Clause"
@@ -31,21 +31,21 @@ lookyloo = 'pylookyloo:main'
31
31
 
32
32
  [tool.poetry.dependencies]
33
33
  python = "^3.8"
34
- requests = "^2.31.0"
34
+ requests = "^2.32.3"
35
35
  Sphinx = [
36
36
  {version = "<7.2", python = "<3.9", optional = true},
37
37
  {version = "^7.2", python = ">=3.9", optional = true}
38
38
  ]
39
39
 
40
40
  [tool.poetry.group.dev.dependencies]
41
- mypy = "^1.9.0"
42
- types-requests = "^2.31.0.20240311"
41
+ mypy = "^1.10.1"
42
+ types-requests = "^2.32.0.20240622"
43
43
  ipython = [
44
44
  {version = "<8.13.0", python = "<3.9"},
45
45
  {version = "^8.18.0", python = ">=3.9"},
46
46
  {version = "^8.19.0", python = ">=3.10"}
47
47
  ]
48
- pytest = "^8.1.1"
48
+ pytest = "^8.2.2"
49
49
 
50
50
  [tool.poetry.extras]
51
51
  docs = ["Sphinx"]
File without changes
File without changes