dissect.target 3.19.dev32__py3-none-any.whl → 3.19.dev34__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,27 +1,35 @@
1
+ from __future__ import annotations
2
+
1
3
  import ast
2
4
  import importlib.machinery
3
5
  import importlib.util
4
6
  import logging
5
7
  from pathlib import Path
6
8
  from types import ModuleType
7
- from typing import Optional, Union
8
9
 
9
10
  log = logging.getLogger(__name__)
10
11
 
11
12
  CONFIG_NAME = ".targetcfg.py"
12
13
 
13
14
 
14
- def load(path: Optional[Union[Path, str]]) -> ModuleType:
15
+ def load(paths: list[Path | str] | Path | str | None) -> ModuleType:
16
+ """Attempt to load one configuration from the provided path(s)."""
17
+
18
+ if isinstance(paths, Path) or isinstance(paths, str):
19
+ paths = [paths]
20
+
15
21
  config_spec = importlib.machinery.ModuleSpec("config", None)
16
22
  config = importlib.util.module_from_spec(config_spec)
17
- config_file = _find_config_file(path)
23
+ config_file = _find_config_file(paths)
24
+
18
25
  if config_file:
19
26
  config_values = _parse_ast(config_file.read_bytes())
20
27
  config.__dict__.update(config_values)
28
+
21
29
  return config
22
30
 
23
31
 
24
- def _parse_ast(code: str) -> dict[str, Union[str, int]]:
32
+ def _parse_ast(code: str) -> dict[str, str | int]:
25
33
  # Only allow basic value assignments for backwards compatibility
26
34
  obj = {}
27
35
 
@@ -49,15 +57,19 @@ def _parse_ast(code: str) -> dict[str, Union[str, int]]:
49
57
  return obj
50
58
 
51
59
 
52
- def _find_config_file(path: Optional[Union[Path, str]]) -> Optional[Path]:
53
- """Find a config file anywhere in the given path and return it.
60
+ def _find_config_file(paths: list[Path | str] | None) -> Path | None:
61
+ """Find a config file anywhere in the given path(s) and return it.
54
62
 
55
63
  This algorithm allows parts of the path to not exist or the last part to be a filename.
56
64
  It also does not look in the root directory ('/') for config files.
57
65
  """
58
66
 
67
+ if not paths:
68
+ return
69
+
59
70
  config_file = None
60
- if path:
71
+
72
+ for path in paths:
61
73
  path = Path(path)
62
74
  cur_path = path.absolute()
63
75
 
@@ -69,4 +81,7 @@ def _find_config_file(path: Optional[Union[Path, str]]) -> Optional[Path]:
69
81
  config_file = cur_config
70
82
  cur_path = cur_path.parent
71
83
 
84
+ if config_file:
85
+ break
86
+
72
87
  return config_file
@@ -5,7 +5,7 @@ import re
5
5
  import urllib
6
6
  from os import PathLike
7
7
  from pathlib import Path
8
- from typing import TYPE_CHECKING, BinaryIO, Optional, Union
8
+ from typing import TYPE_CHECKING, BinaryIO
9
9
 
10
10
  from dissect.target.exceptions import FileNotFoundError
11
11
  from dissect.target.filesystem import Filesystem
@@ -42,12 +42,31 @@ def add_virtual_ntfs_filesystem(
42
42
  fh_sds = _try_open(fs, sds_path)
43
43
 
44
44
  if any([fh_boot, fh_mft]):
45
- ntfs = NtfsFilesystem(boot=fh_boot, mft=fh_mft, usnjrnl=fh_usnjrnl, sds=fh_sds)
46
- target.filesystems.add(ntfs)
47
- fs.ntfs = ntfs.ntfs
45
+ ntfs = None
48
46
 
49
-
50
- def _try_open(fs: Filesystem, path: str) -> BinaryIO:
47
+ try:
48
+ ntfs = NtfsFilesystem(boot=fh_boot, mft=fh_mft, usnjrnl=fh_usnjrnl, sds=fh_sds)
49
+ except Exception as e:
50
+ if fh_boot:
51
+ log.warning("Failed to load NTFS filesystem from %s, retrying without $Boot file", fs)
52
+ log.debug("", exc_info=e)
53
+
54
+ try:
55
+ # Try once more without the $Boot file
56
+ ntfs = NtfsFilesystem(mft=fh_mft, usnjrnl=fh_usnjrnl, sds=fh_sds)
57
+ except Exception:
58
+ log.warning("Failed to load NTFS filesystem from %s without $Boot file, skipping", fs)
59
+ return
60
+
61
+ # Only add it if we have a valid NTFS with an MFT
62
+ if ntfs and ntfs.ntfs.mft:
63
+ target.filesystems.add(ntfs)
64
+ fs.ntfs = ntfs.ntfs
65
+ else:
66
+ log.warning("Opened NTFS filesystem from %s but could not find $MFT, skipping", fs)
67
+
68
+
69
+ def _try_open(fs: Filesystem, path: str) -> BinaryIO | None:
51
70
  paths = [path] if not isinstance(path, list) else path
52
71
 
53
72
  for path in paths:
@@ -61,7 +80,7 @@ def _try_open(fs: Filesystem, path: str) -> BinaryIO:
61
80
  pass
62
81
 
63
82
 
64
- def extract_path_info(path: Union[str, Path]) -> tuple[Path, Optional[urllib.parse.ParseResult]]:
83
+ def extract_path_info(path: str | Path) -> tuple[Path, urllib.parse.ParseResult | None]:
65
84
  """
66
85
  Extracts a ParseResult from a path if it has
67
86
  a scheme and adjusts the path if necessary.
dissect/target/target.py CHANGED
@@ -87,9 +87,10 @@ class Target:
87
87
  self._applied = False
88
88
 
89
89
  try:
90
- self._config = config.load(self.path)
90
+ self._config = config.load([self.path, os.getcwd()])
91
91
  except Exception as e:
92
- self.log.debug("Error loading config file", exc_info=e)
92
+ self.log.warning("Error loading config file: %s", self.path)
93
+ self.log.debug("", exc_info=e)
93
94
  self._config = config.load(None) # This loads an empty config.
94
95
 
95
96
  # Fill the disks and/or volumes and/or filesystems and apply() will
@@ -58,6 +58,7 @@ try:
58
58
  except ImportError:
59
59
  # Readline is not available on Windows
60
60
  log.warning("Readline module is not available")
61
+ readline = None
61
62
 
62
63
  # ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
63
64
  STAT_TEMPLATE = """ File: {path} {symlink}
@@ -111,12 +112,43 @@ class TargetCmd(cmd.Cmd):
111
112
 
112
113
  CMD_PREFIX = "cmd_"
113
114
 
115
+ DEFAULT_HISTFILE = "~/.dissect_history"
116
+ DEFAULT_HISTFILESIZE = 10_000
117
+ DEFAULT_HISTDIR = None
118
+ DEFAULT_HISTDIRFMT = ".dissect_history_{uid}_{target}"
119
+
114
120
  def __init__(self, target: Target):
115
121
  cmd.Cmd.__init__(self)
116
122
  self.target = target
117
123
  self.debug = False
118
124
  self.identchars += "."
119
125
 
126
+ self.histfilesize = getattr(target._config, "HISTFILESIZE", self.DEFAULT_HISTFILESIZE)
127
+ self.histdir = getattr(target._config, "HISTDIR", self.DEFAULT_HISTDIR)
128
+
129
+ if self.histdir:
130
+ self.histdirfmt = getattr(target._config, "HISTDIRFMT", self.DEFAULT_HISTDIRFMT)
131
+ self.histfile = pathlib.Path(self.histdir).resolve() / pathlib.Path(
132
+ self.histdirfmt.format(uid=os.getuid(), target=target.name)
133
+ )
134
+ else:
135
+ self.histfile = pathlib.Path(getattr(target._config, "HISTFILE", self.DEFAULT_HISTFILE)).expanduser()
136
+
137
+ def preloop(self) -> None:
138
+ if readline and self.histfile.exists():
139
+ try:
140
+ readline.read_history_file(self.histfile)
141
+ except Exception as e:
142
+ log.debug("Error reading history file: %s", e)
143
+
144
+ def postloop(self) -> None:
145
+ if readline:
146
+ readline.set_history_length(self.histfilesize)
147
+ try:
148
+ readline.write_history_file(self.histfile)
149
+ except Exception as e:
150
+ log.debug("Error writing history file: %s", e)
151
+
120
152
  def __getattr__(self, attr: str) -> Any:
121
153
  if attr.startswith("help_"):
122
154
  _, _, command = attr.partition("_")
@@ -1241,10 +1273,11 @@ def run_cli(cli: cmd.Cmd) -> None:
1241
1273
  # Print an empty newline on exit
1242
1274
  print()
1243
1275
  return
1276
+
1244
1277
  except KeyboardInterrupt:
1245
1278
  # Add a line when pressing ctrl+c, so the next one starts at a new line
1246
1279
  print()
1247
- pass
1280
+
1248
1281
  except Exception as e:
1249
1282
  if cli.debug:
1250
1283
  log.exception(e)
@@ -1252,7 +1285,6 @@ def run_cli(cli: cmd.Cmd) -> None:
1252
1285
  log.info(e)
1253
1286
  print(f"*** Unhandled error: {e}")
1254
1287
  print("If you wish to see the full debug trace, enable debug mode.")
1255
- pass
1256
1288
 
1257
1289
 
1258
1290
  @catch_sigpipe
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev32
3
+ Version: 3.19.dev34
4
4
  Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -5,7 +5,7 @@ dissect/target/filesystem.py,sha256=G1gbOUpnQZyovubYGEUKgaDV0eHH5vE83-0gTc5PZAM,
5
5
  dissect/target/loader.py,sha256=I8WNzDA0SMy42F7zfyBcSKj_VKNv64213WUvtGZ77qE,7374
6
6
  dissect/target/plugin.py,sha256=HAN8maaDt-Rlqt8Rr1IW7gXQpzNQZjCVz-i4aSPphSw,48677
7
7
  dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
8
- dissect/target/target.py,sha256=KZ3vDsMjrXxEP6sQE1kOlxMNjqFFsxnivYhoX26GBEY,32363
8
+ dissect/target/target.py,sha256=nqd5OuVTwYR7XSzlMp7SHQoRCgZD_Z7QshnEEKcX-tw,32426
9
9
  dissect/target/volume.py,sha256=aQZAJiny8jjwkc9UtwIRwy7nINXjCxwpO-_UDfh6-BA,15801
10
10
  dissect/target/containers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  dissect/target/containers/asdf.py,sha256=DJp0QEFwUjy2MFwKYcYqIR_BS1fQT1Yi9Kcmqt0aChM,1366
@@ -45,7 +45,7 @@ dissect/target/filesystems/xfs.py,sha256=kIyFGKYlyFYC7H3jaEv-lNKtBW4ZkD92H0WpfGc
45
45
  dissect/target/filesystems/zip.py,sha256=WT1bQhzX_1MXXVZTKrJniae4xqRqMZ8FsfbvhgGQRTQ,4462
46
46
  dissect/target/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  dissect/target/helpers/cache.py,sha256=TXlJBdFRz6V9zKs903am4Yawr0maYw5kZY0RqklDQJM,8568
48
- dissect/target/helpers/config.py,sha256=6917CZ6eDHaK_tOoiVEIndyhRXO6r6eCBIleq6f47PQ,2346
48
+ dissect/target/helpers/config.py,sha256=RMHnIuKJHINHiLrvKN3EyA0jFA1o6-pbeaycG8Pgrp8,2596
49
49
  dissect/target/helpers/configutil.py,sha256=AEnkMQ0e6PncvCqGa-ACzBQWQBhMGBCzO5qzGJtRu60,27644
50
50
  dissect/target/helpers/cyber.py,sha256=WnJlk-HqAETmDAgLq92JPxyDLxvzSoFV_WrO-odVKBI,16805
51
51
  dissect/target/helpers/descriptor_extensions.py,sha256=uT8GwznfDAiIgMM7JKKOY0PXKMv2c0GCqJTCkWFgops,2605
@@ -54,7 +54,7 @@ dissect/target/helpers/fsutil.py,sha256=tPyH4RBDqM9QXjamIQaDRLUy3b4dKmfrT6k3ZP01
54
54
  dissect/target/helpers/hashutil.py,sha256=bYAGEjyYyxuCTziO4kCx6srzY1Cm-PXmayRRcxt5ca4,1061
55
55
  dissect/target/helpers/keychain.py,sha256=wYH0sf7eaxP0bZTo80RF_BQMWulCWmIQ8Tzt9K5TSNQ,3611
56
56
  dissect/target/helpers/lazy.py,sha256=823VtmdWsbJyVZvNWopDhQdqq2i1xtj6b8IKfveboKw,1771
57
- dissect/target/helpers/loaderutil.py,sha256=kiyMWra_gVxfNSGwLlgxLcuuqAYuCMDc5NiCDprVNnc,2649
57
+ dissect/target/helpers/loaderutil.py,sha256=4cS0RKGgsljQYYc5uGzmnWJ_NXt7QfWJ1jvtEINZmdE,3415
58
58
  dissect/target/helpers/localeutil.py,sha256=Y4Fh4jDSGfm5356xSLMriUCN8SZP_FAHg_iodkAxNq4,1504
59
59
  dissect/target/helpers/mount.py,sha256=JxhUYyEbDnHfzPpfuWy4nV9OwCJPoDSGdHHNiyvd_l0,3949
60
60
  dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q,22258
@@ -331,7 +331,7 @@ dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLc
331
331
  dissect/target/tools/mount.py,sha256=L_0tSmiBdW4aSaF0vXjB0bAkTC0kmT2N1hrbW6s5Jow,3254
332
332
  dissect/target/tools/query.py,sha256=ONHu2FVomLccikb84qBrlhNmEfRoHYFQMcahk_y2c9A,15580
333
333
  dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
334
- dissect/target/tools/shell.py,sha256=_widEuIRqZhYzcFR52NYI8O2aPFm6tG5Uiv-AIrC32U,45155
334
+ dissect/target/tools/shell.py,sha256=sjoc4nI9fsU5ZG7nDPNZvE-RtIPTwREbVdbg8WA3XTo,46442
335
335
  dissect/target/tools/utils.py,sha256=sQizexY3ui5vmWw4KOBLg5ecK3TPFjD-uxDqRn56ZTY,11304
336
336
  dissect/target/tools/yara.py,sha256=70k-2VMulf1EdkX03nCACzejaOEcsFHOyX-4E40MdQU,2044
337
337
  dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -346,10 +346,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
346
346
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
347
347
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
348
348
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
349
- dissect.target-3.19.dev32.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
350
- dissect.target-3.19.dev32.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
351
- dissect.target-3.19.dev32.dist-info/METADATA,sha256=aYH5ExmLUxmscik9N5OAaWx16j-W7AHvqsONGGGOZoE,12719
352
- dissect.target-3.19.dev32.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
353
- dissect.target-3.19.dev32.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
354
- dissect.target-3.19.dev32.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
355
- dissect.target-3.19.dev32.dist-info/RECORD,,
349
+ dissect.target-3.19.dev34.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
350
+ dissect.target-3.19.dev34.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
351
+ dissect.target-3.19.dev34.dist-info/METADATA,sha256=fiUYaR4jBceVkr1ddarm8Xyn0sU8feK-AvW5l7JAbXU,12719
352
+ dissect.target-3.19.dev34.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
353
+ dissect.target-3.19.dev34.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
354
+ dissect.target-3.19.dev34.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
355
+ dissect.target-3.19.dev34.dist-info/RECORD,,