acquire 3.17.dev7__tar.gz → 3.18.dev2__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.
Files changed (68) hide show
  1. {acquire-3.17.dev7/acquire.egg-info → acquire-3.18.dev2}/PKG-INFO +2 -2
  2. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/uploaders/minio.py +6 -2
  3. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/version.py +2 -2
  4. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/volatilestream.py +35 -2
  5. {acquire-3.17.dev7 → acquire-3.18.dev2/acquire.egg-info}/PKG-INFO +2 -2
  6. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire.egg-info/SOURCES.txt +1 -0
  7. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_minio_uploader.py +33 -0
  8. acquire-3.18.dev2/tests/test_volatile.py +20 -0
  9. {acquire-3.17.dev7 → acquire-3.18.dev2}/COPYRIGHT +0 -0
  10. {acquire-3.17.dev7 → acquire-3.18.dev2}/LICENSE +0 -0
  11. {acquire-3.17.dev7 → acquire-3.18.dev2}/MANIFEST.in +0 -0
  12. {acquire-3.17.dev7 → acquire-3.18.dev2}/README.md +0 -0
  13. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/__init__.py +0 -0
  14. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/acquire.py +0 -0
  15. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/collector.py +0 -0
  16. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/crypt.py +0 -0
  17. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/__init__.py +0 -0
  18. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/windows/__init__.py +0 -0
  19. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/windows/collect.py +0 -0
  20. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/windows/exceptions.py +0 -0
  21. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/windows/handles.py +0 -0
  22. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/windows/named_objects.py +0 -0
  23. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/windows/ntdll.py +0 -0
  24. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/dynamic/windows/types.py +0 -0
  25. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/esxi.py +0 -0
  26. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/gui/__init__.py +0 -0
  27. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/gui/base.py +0 -0
  28. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/gui/win32.py +0 -0
  29. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/hashes.py +0 -0
  30. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/log.py +0 -0
  31. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/outputs/__init__.py +0 -0
  32. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/outputs/base.py +0 -0
  33. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/outputs/dir.py +0 -0
  34. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/outputs/tar.py +0 -0
  35. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/outputs/zip.py +0 -0
  36. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/tools/__init__.py +0 -0
  37. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/tools/decrypter.py +0 -0
  38. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/uploaders/__init__.py +0 -0
  39. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/uploaders/plugin.py +0 -0
  40. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/uploaders/plugin_registry.py +0 -0
  41. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire/utils.py +0 -0
  42. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire.egg-info/dependency_links.txt +0 -0
  43. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire.egg-info/entry_points.txt +0 -0
  44. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire.egg-info/requires.txt +0 -0
  45. {acquire-3.17.dev7 → acquire-3.18.dev2}/acquire.egg-info/top_level.txt +0 -0
  46. {acquire-3.17.dev7 → acquire-3.18.dev2}/pyproject.toml +0 -0
  47. {acquire-3.17.dev7 → acquire-3.18.dev2}/setup.cfg +0 -0
  48. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/__init__.py +0 -0
  49. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/conftest.py +0 -0
  50. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/data/private_key.pem +0 -0
  51. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/data/public_key.pem +0 -0
  52. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/docs/Makefile +0 -0
  53. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/docs/conf.py +0 -0
  54. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/docs/index.rst +0 -0
  55. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_acquire_command.py +0 -0
  56. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_acquire_modules.py +0 -0
  57. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_collector.py +0 -0
  58. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_decryptor_funcs.py +0 -0
  59. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_esxi_memory.py +0 -0
  60. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_file_sorting.py +0 -0
  61. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_gui.py +0 -0
  62. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_misc_users.py +0 -0
  63. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_outputs_dir.py +0 -0
  64. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_outputs_tar.py +0 -0
  65. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_outputs_zip.py +0 -0
  66. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_plugin.py +0 -0
  67. {acquire-3.17.dev7 → acquire-3.18.dev2}/tests/test_utils.py +0 -0
  68. {acquire-3.17.dev7 → acquire-3.18.dev2}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: acquire
3
- Version: 3.17.dev7
3
+ Version: 3.18.dev2
4
4
  Summary: A tool to quickly gather forensic artifacts from disk images or a live system into a lightweight container
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -1,4 +1,3 @@
1
- import os
2
1
  from pathlib import Path
3
2
  from typing import Any, Optional
4
3
 
@@ -20,6 +19,7 @@ class MinIO(UploaderPlugin):
20
19
  self.access_id = upload.get("access_id")
21
20
  self.access_key = upload.get("access_key")
22
21
  self.bucket_name = upload.get("bucket")
22
+ self.folder = upload.get("folder", "").rstrip("/")
23
23
 
24
24
  if not all((self.endpoint, self.access_id, self.access_key, self.bucket_name)):
25
25
  raise ValueError("Invalid cloud upload configuration")
@@ -45,7 +45,11 @@ class MinIO(UploaderPlugin):
45
45
  return Minio(self.endpoint, self.access_id, self.access_key, http_client=http_client)
46
46
 
47
47
  def upload_file(self, client: Any, path: Path) -> None:
48
- client.fput_object(self.bucket_name, os.path.basename(path), path)
48
+ object_path = path.name
49
+ if self.folder:
50
+ object_path = f"{self.folder}/{object_path}"
51
+
52
+ client.fput_object(self.bucket_name, object_path, path)
49
53
 
50
54
  def finish(self, client: Any) -> None:
51
55
  pass
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.17.dev7'
16
- __version_tuple__ = version_tuple = (3, 17, 'dev7')
15
+ __version__ = version = '3.18.dev2'
16
+ __version_tuple__ = version_tuple = (3, 18, 'dev2')
@@ -1,7 +1,9 @@
1
1
  import os
2
+ from concurrent import futures
2
3
  from io import SEEK_SET, UnsupportedOperation
3
4
  from pathlib import Path
4
5
  from stat import S_IRGRP, S_IROTH, S_IRUSR
6
+ from typing import Any, Callable
5
7
 
6
8
  from dissect.util.stream import AlignedStream
7
9
 
@@ -14,6 +16,35 @@ except ImportError:
14
16
  HAS_FCNTL = False
15
17
 
16
18
 
19
+ def timeout(func: Callable, *, timelimit: int) -> Callable:
20
+ """Timeout a function if it takes too long to complete.
21
+
22
+ Args:
23
+ func: a function to wrap.
24
+ timelimit: The time in seconds that an operation is allowed to run.
25
+
26
+ Raises:
27
+ TimeoutError: If its time exceeds the timelimit
28
+ """
29
+
30
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
31
+ with futures.ThreadPoolExecutor(max_workers=1) as executor:
32
+ future = executor.submit(func, *args, **kwargs)
33
+
34
+ try:
35
+ result = future.result(timelimit)
36
+ except futures.TimeoutError:
37
+ raise TimeoutError
38
+ finally:
39
+ # Make sure the thread stops right away.
40
+ executor._threads.clear()
41
+ futures.thread._threads_queues.clear()
42
+
43
+ return result
44
+
45
+ return wrapper
46
+
47
+
17
48
  class VolatileStream(AlignedStream):
18
49
  """Streaming class to handle various procfs and sysfs edge-cases. Backed by `AlignedStream`.
19
50
 
@@ -41,6 +72,8 @@ class VolatileStream(AlignedStream):
41
72
  st_mode = os.fstat(self.fd).st_mode
42
73
  write_only = (st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0 # novermin
43
74
 
75
+ self._os_read = timeout(os.read, timelimit=5)
76
+
44
77
  super().__init__(0 if write_only else size)
45
78
 
46
79
  def seek(self, pos: int, whence: int = SEEK_SET) -> int:
@@ -53,8 +86,8 @@ class VolatileStream(AlignedStream):
53
86
  result = []
54
87
  while length:
55
88
  try:
56
- buf = os.read(self.fd, min(length, self.size - offset))
57
- except BlockingIOError:
89
+ buf = self._os_read(self.fd, min(length, self.size - offset))
90
+ except (BlockingIOError, TimeoutError):
58
91
  break
59
92
 
60
93
  if not buf:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: acquire
3
- Version: 3.17.dev7
3
+ Version: 3.18.dev2
4
4
  Summary: A tool to quickly gather forensic artifacts from disk images or a live system into a lightweight container
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -58,6 +58,7 @@ tests/test_outputs_tar.py
58
58
  tests/test_outputs_zip.py
59
59
  tests/test_plugin.py
60
60
  tests/test_utils.py
61
+ tests/test_volatile.py
61
62
  tests/data/private_key.pem
62
63
  tests/data/public_key.pem
63
64
  tests/docs/Makefile
@@ -1,4 +1,5 @@
1
1
  from pathlib import Path
2
+ from typing import Callable
2
3
  from unittest.mock import Mock, patch
3
4
 
4
5
  import pytest
@@ -79,3 +80,35 @@ def test_upload_file_multiple_failures(minio_instance: MinIO):
79
80
  with patch("acquire.uploaders.plugin.log") as mocked_logger:
80
81
  upload_files_using_uploader(minio_instance, [Path("hello")])
81
82
  mocked_logger.error.assert_called_with("Upload %s FAILED after too many attempts. Stopping.", Path("hello"))
83
+
84
+
85
+ def test_minio_folder_initialization(minio_plugin: Callable) -> None:
86
+ arguments = {"endpoint": "test", "access_id": "test", "access_key": "test", "bucket": "test", "folder": "Uploads/"}
87
+ minio = minio_plugin(upload=arguments)
88
+ assert minio.folder == "Uploads"
89
+
90
+ arguments["folder"] = "Uploads/test_folder"
91
+ minio_no_backslash = minio_plugin(upload=arguments)
92
+ assert minio_no_backslash.folder == "Uploads/test_folder"
93
+
94
+ arguments.pop("folder")
95
+ minio_no_folder = minio_plugin(upload=arguments)
96
+ assert minio_no_folder.folder == ""
97
+
98
+
99
+ def test_upload_file_with_folder(minio_instance: MinIO):
100
+ mock_client = Mock()
101
+ test_path = Path("example.txt")
102
+ minio_instance.folder = "test_folder"
103
+ minio_instance.upload_file(mock_client, test_path)
104
+
105
+ mock_client.fput_object.assert_called_once_with("test", "test_folder/example.txt", test_path)
106
+
107
+
108
+ def test_upload_file_without_folder(minio_instance: MinIO):
109
+ mock_client = Mock()
110
+ test_path = Path("example.txt")
111
+ minio_instance.folder = ""
112
+ minio_instance.upload_file(mock_client, test_path)
113
+
114
+ mock_client.fput_object.assert_called_once_with("test", "example.txt", test_path)
@@ -0,0 +1,20 @@
1
+ from time import sleep, time
2
+
3
+ import pytest
4
+
5
+ from acquire.volatilestream import timeout
6
+
7
+
8
+ def test_timeout():
9
+ def snooze():
10
+ sleep(10)
11
+
12
+ function = timeout(snooze, timelimit=5)
13
+ start = time()
14
+
15
+ with pytest.raises(TimeoutError):
16
+ function()
17
+
18
+ end = time()
19
+
20
+ assert end - start < 6
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