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

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,11 +3,11 @@ from __future__ import annotations
3
3
  import io
4
4
  import textwrap
5
5
  from logging import getLogger
6
- from typing import Any, BinaryIO, Iterator, Optional, Union
6
+ from typing import Any, BinaryIO, Iterator, Optional
7
7
 
8
8
  from dissect.target import Target
9
9
  from dissect.target.exceptions import ConfigurationParsingError, FileNotFoundError
10
- from dissect.target.filesystem import Filesystem, FilesystemEntry, VirtualFilesystem
10
+ from dissect.target.filesystem import FilesystemEntry, VirtualFilesystem
11
11
  from dissect.target.helpers import fsutil
12
12
  from dissect.target.helpers.configutil import ConfigurationParser, parse
13
13
 
@@ -46,7 +46,7 @@ class ConfigurationFilesystem(VirtualFilesystem):
46
46
  super().__init__(**kwargs)
47
47
  self.root.top = target.fs.get(path)
48
48
 
49
- def _get_till_file(self, path: str, relentry: FilesystemEntry) -> tuple[list[str], FilesystemEntry]:
49
+ def _get_till_file(self, path: str, relentry: FilesystemEntry | None) -> tuple[list[str], FilesystemEntry]:
50
50
  """Searches for the file entry that is pointed to by ``path``.
51
51
 
52
52
  The ``path`` could contain ``key`` entries too, so it searches for the entry from
@@ -56,9 +56,13 @@ class ConfigurationFilesystem(VirtualFilesystem):
56
56
  A list of ``parts`` containing keys: [keys, into, the, file].
57
57
  And the resolved entry: Entry(filename)
58
58
  """
59
+
59
60
  entry = relentry or self.root
61
+ root_path = relentry.path if relentry else self.root.top.path
60
62
 
61
- path = fsutil.normalize(path, alt_separator=self.alt_separator).strip("/")
63
+ # Calculate the relative path
64
+ relpath = fsutil.relpath(path, root_path, alt_separator=self.alt_separator)
65
+ path = fsutil.normalize(relpath, alt_separator=self.alt_separator).strip("/")
62
66
 
63
67
  if not path:
64
68
  return [], entry
@@ -85,10 +89,8 @@ class ConfigurationFilesystem(VirtualFilesystem):
85
89
 
86
90
  return parts[idx:], entry
87
91
 
88
- def get(
89
- self, path: str, relentry: Optional[FilesystemEntry] = None, *args, **kwargs
90
- ) -> Union[FilesystemEntry, ConfigurationEntry]:
91
- """Retrieve a :class:`ConfigurationEntry` or :class:`.FilesystemEntry` relative to the root or ``relentry``.
92
+ def get(self, path: str, relentry: Optional[FilesystemEntry] = None, *args, **kwargs) -> ConfigurationEntry:
93
+ """Retrieve a :class:`ConfigurationEntry` relative to the root or ``relentry``.
92
94
 
93
95
  Raises:
94
96
  FileNotFoundError: if it could not find the entry.
@@ -96,7 +98,7 @@ class ConfigurationFilesystem(VirtualFilesystem):
96
98
  parts, entry = self._get_till_file(path, relentry)
97
99
 
98
100
  if entry.is_dir():
99
- return entry
101
+ return ConfigurationEntry(self, entry.path, entry, None)
100
102
 
101
103
  entry = self._convert_entry(entry, *args, **kwargs)
102
104
 
@@ -108,23 +110,21 @@ class ConfigurationFilesystem(VirtualFilesystem):
108
110
 
109
111
  return entry
110
112
 
111
- def _convert_entry(
112
- self, file_entry: FilesystemEntry, *args, **kwargs
113
- ) -> Union[ConfigurationEntry, FilesystemEntry]:
113
+ def _convert_entry(self, file_entry: FilesystemEntry, *args, **kwargs) -> ConfigurationEntry:
114
114
  """Creates a :class:`ConfigurationEntry` from a ``file_entry``.
115
115
 
116
116
  If an error occurs during the parsing of the file contents,
117
117
  the original ``file_entry`` is returned.
118
118
  """
119
119
  entry = file_entry
120
+ config_parser = None
120
121
  try:
121
122
  config_parser = parse(entry, *args, **kwargs)
122
- entry = ConfigurationEntry(self, entry.path, entry, config_parser)
123
123
  except ConfigurationParsingError as e:
124
124
  # If a parsing error gets created, it should return the `entry`
125
125
  log.debug("Error when parsing %s with message '%s'", entry.path, e)
126
126
 
127
- return entry
127
+ return ConfigurationEntry(self, entry.path, entry, config_parser)
128
128
 
129
129
 
130
130
  class ConfigurationEntry(FilesystemEntry):
@@ -163,10 +163,10 @@ class ConfigurationEntry(FilesystemEntry):
163
163
 
164
164
  def __init__(
165
165
  self,
166
- fs: Filesystem,
166
+ fs: ConfigurationFilesystem,
167
167
  path: str,
168
168
  entry: FilesystemEntry,
169
- parser_items: Optional[Union[dict, ConfigurationParser, str, list]] = None,
169
+ parser_items: dict | ConfigurationParser | str | list | None = None,
170
170
  ) -> None:
171
171
  super().__init__(fs, path, entry)
172
172
  self.parser_items = parser_items
@@ -182,7 +182,7 @@ class ConfigurationEntry(FilesystemEntry):
182
182
 
183
183
  return f"<{self.__class__.__name__} {output}"
184
184
 
185
- def get(self, key, default: Optional[Any] = None) -> Union[ConfigurationEntry, Any, None]:
185
+ def get(self, key, default: Any | None = None) -> ConfigurationEntry | Any | None:
186
186
  """Gets the dictionary key that belongs to this entry using ``key``.
187
187
  Behaves like ``dictionary.get()``.
188
188
 
@@ -197,13 +197,19 @@ class ConfigurationEntry(FilesystemEntry):
197
197
  if not key:
198
198
  raise TypeError("key should be defined")
199
199
 
200
- if key in self.parser_items:
200
+ path = fsutil.join(self.path, key, alt_separator=self.fs.alt_separator)
201
+
202
+ if self.parser_items and key in self.parser_items:
201
203
  return ConfigurationEntry(
202
204
  self.fs,
203
- fsutil.join(self.path, key, alt_separator=self.fs.alt_separator),
205
+ path,
204
206
  self.entry,
205
207
  self.parser_items[key],
206
208
  )
209
+
210
+ if self.entry.is_dir():
211
+ return self.fs.get(path, self.entry)
212
+
207
213
  return default
208
214
 
209
215
  def _write_value_mapping(self, values: dict[str, Any], indentation_nr: int = 0) -> str:
@@ -257,6 +263,11 @@ class ConfigurationEntry(FilesystemEntry):
257
263
  if self.is_file():
258
264
  raise NotADirectoryError()
259
265
 
266
+ if self.parser_items is None and self.entry.is_dir():
267
+ for entry in self.entry.scandir():
268
+ yield ConfigurationEntry(self.fs, entry.name, entry, None)
269
+ return
270
+
260
271
  for key, values in self.parser_items.items():
261
272
  yield ConfigurationEntry(self.fs, key, self.entry, values)
262
273
 
@@ -266,7 +277,7 @@ class ConfigurationEntry(FilesystemEntry):
266
277
  def is_dir(self, follow_symlinks: bool = True) -> bool:
267
278
  """Returns whether this :class:`ConfigurationEntry` can be considered a directory."""
268
279
  # if self.parser_items has keys (thus sub-values), we can consider it a directory.
269
- return hasattr(self.parser_items, "keys")
280
+ return (self.parser_items is None and self.entry.is_dir()) or hasattr(self.parser_items, "keys")
270
281
 
271
282
  def is_symlink(self) -> bool:
272
283
  """Return whether this :class:`ConfigurationEntry` is a symlink or not.
@@ -284,7 +295,7 @@ class ConfigurationEntry(FilesystemEntry):
284
295
  Returns:
285
296
  Whether the ``entry`` and ``key`` exists
286
297
  """
287
- return self.entry.exists() and key in self.parser_items
298
+ return self.parser_items and self.entry.exists() and key in self.parser_items
288
299
 
289
300
  def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
290
301
  """Returns the stat from the underlying :class:`.FilesystemEntry` :attr:`entry`."""
@@ -171,7 +171,7 @@ class ConfigurationParser:
171
171
  try:
172
172
  self.parse_file(fh)
173
173
  except Exception as e:
174
- raise ConfigurationParsingError(*e.args) from e
174
+ raise ConfigurationParsingError(e.args) from e
175
175
 
176
176
  if self.collapse_all or self.collapse:
177
177
  self.parsed_data = self._collapse_dict(self.parsed_data)
@@ -62,13 +62,13 @@ class EtcTree(ConfigurationTreePlugin):
62
62
 
63
63
  @export(record=UnixConfigTreeRecord)
64
64
  @arg("--glob", dest="pattern", required=False, default="*", type=str, help="Glob-style pattern to search for")
65
- def etc(self, pattern: str) -> Iterator[UnixConfigTreeRecord]:
66
- for entry, subs, items in self.config_fs.walk("/"):
65
+ @arg("--root", dest="root", required=False, default="/", type=str, help="Path to use as root for search")
66
+ def etc(self, pattern: str, root: str) -> Iterator[UnixConfigTreeRecord]:
67
+ for entry, subs, items in self.config_fs.walk(root):
67
68
  for item in items:
68
69
  try:
69
70
  config_object = self.get(str(Path(entry) / Path(item)))
70
- if isinstance(config_object, ConfigurationEntry):
71
- yield from self._sub(config_object, Path(entry) / Path(item), pattern)
71
+ yield from self._sub(config_object, Path(entry) / Path(item), pattern)
72
72
  except Exception:
73
73
  self.target.log.warning("Could not open configuration item: %s", item)
74
74
  pass
@@ -12,6 +12,7 @@ from dissect.target.exceptions import TargetError
12
12
  from dissect.target.helpers.record import TargetRecordDescriptor
13
13
  from dissect.target.tools.query import record_output
14
14
  from dissect.target.tools.utils import (
15
+ args_to_uri,
15
16
  catch_sigpipe,
16
17
  configure_generic_arguments,
17
18
  process_generic_arguments,
@@ -50,14 +51,14 @@ def main():
50
51
  )
51
52
  parser.add_argument("targets", metavar="TARGETS", nargs="*", help="Targets to display info from")
52
53
  parser.add_argument("--from-file", nargs="?", type=Path, help="file containing targets to load")
53
- parser.add_argument("-d", "--delimiter", default=" ", action="store", metavar="','")
54
54
  parser.add_argument("-s", "--strings", action="store_true", help="print output as string")
55
55
  parser.add_argument("-r", "--record", action="store_true", help="print output as record")
56
56
  parser.add_argument("-j", "--json", action="store_true", help="output records as pretty json")
57
57
  parser.add_argument("-J", "--jsonlines", action="store_true", help="output records as one-line json")
58
+ parser.add_argument("-L", "--loader", action="store", default=None, help="select a specific loader (i.e. vmx, raw)")
58
59
  configure_generic_arguments(parser)
59
60
 
60
- args = parser.parse_args()
61
+ args, rest = parser.parse_known_args()
61
62
 
62
63
  process_generic_arguments(args)
63
64
 
@@ -73,8 +74,10 @@ def main():
73
74
  targets = targets[:-1]
74
75
  args.targets = targets
75
76
 
77
+ targets = args_to_uri(args.targets, args.loader, rest) if args.loader else args.targets
78
+
76
79
  try:
77
- for i, target in enumerate(Target.open_all(args.targets)):
80
+ for i, target in enumerate(Target.open_all(targets)):
78
81
  try:
79
82
  if args.jsonlines:
80
83
  print(json.dumps(get_target_info(target), default=str))
@@ -8,6 +8,7 @@ from dissect.target.exceptions import TargetError
8
8
  from dissect.target.plugins.filesystem.yara import HAS_YARA, YaraPlugin
9
9
  from dissect.target.tools.query import record_output
10
10
  from dissect.target.tools.utils import (
11
+ args_to_uri,
11
12
  catch_sigpipe,
12
13
  configure_generic_arguments,
13
14
  process_generic_arguments,
@@ -27,6 +28,7 @@ def main():
27
28
 
28
29
  parser.add_argument("targets", metavar="TARGETS", nargs="*", help="Targets to load")
29
30
  parser.add_argument("-s", "--strings", default=False, action="store_true", help="print output as string")
31
+ parser.add_argument("-L", "--loader", action="store", default=None, help="select a specific loader (i.e. vmx, raw)")
30
32
  parser.add_argument("--children", action="store_true", help="include children")
31
33
 
32
34
  for args, kwargs in getattr(YaraPlugin.yara, "__args__", []):
@@ -34,7 +36,7 @@ def main():
34
36
 
35
37
  configure_generic_arguments(parser)
36
38
 
37
- args = parser.parse_args()
39
+ args, rest = parser.parse_known_args()
38
40
  process_generic_arguments(args)
39
41
 
40
42
  if not HAS_YARA:
@@ -45,8 +47,10 @@ def main():
45
47
  log.error("No targets provided")
46
48
  parser.exit(1)
47
49
 
50
+ targets = args_to_uri(args.targets, args.loader, rest) if args.loader else args.targets
51
+
48
52
  try:
49
- for target in Target.open_all(args.targets, args.children):
53
+ for target in Target.open_all(targets, args.children):
50
54
  rs = record_output(args.strings, False)
51
55
  for record in target.yara(args.rules, args.path, args.max_size, args.check):
52
56
  rs.write(record)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev30
3
+ Version: 3.19.dev32
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
@@ -25,7 +25,7 @@ dissect/target/filesystems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
25
25
  dissect/target/filesystems/ad1.py,sha256=nEPzaaRsb6bL4ItFo0uLdmdLvrmK9BjqHeD3FOp3WQI,2413
26
26
  dissect/target/filesystems/btrfs.py,sha256=5MBi193ZvclkEQcxDr_sDHfj_FYU_hyYNRL4YqpDu4M,6243
27
27
  dissect/target/filesystems/cb.py,sha256=6LcoJiwsYu1Han31IUzVpZVDTifhTLTx_gLfNpB_p6k,5329
28
- dissect/target/filesystems/config.py,sha256=C2JnzBzMqbAjchGFDwURItCeUY7uxkhw1Gen-6cGkAc,11432
28
+ dissect/target/filesystems/config.py,sha256=GQOtixIIt-Jjtpl3IVqUTujcBFfWaAZeAtvxgNUNetU,11963
29
29
  dissect/target/filesystems/cpio.py,sha256=ssVCjkAtLn2FqmNxeo6U5boyUdSYFxLWfXpytHYGPqs,641
30
30
  dissect/target/filesystems/dir.py,sha256=rKEreX3A7CI6a3pMssrO9F-9i5pkxCn_Ucs_dMtHxxA,4574
31
31
  dissect/target/filesystems/exfat.py,sha256=PRkZPUVN5NlgB1VetFtywdNgF6Yj5OBtF5a25t-fFvw,5917
@@ -46,7 +46,7 @@ dissect/target/filesystems/zip.py,sha256=WT1bQhzX_1MXXVZTKrJniae4xqRqMZ8FsfbvhgG
46
46
  dissect/target/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  dissect/target/helpers/cache.py,sha256=TXlJBdFRz6V9zKs903am4Yawr0maYw5kZY0RqklDQJM,8568
48
48
  dissect/target/helpers/config.py,sha256=6917CZ6eDHaK_tOoiVEIndyhRXO6r6eCBIleq6f47PQ,2346
49
- dissect/target/helpers/configutil.py,sha256=SLJFt_k5ezm7CIr_mcZqVrV-2YrcX5D48KgyC9oyWGw,27645
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
52
52
  dissect/target/helpers/docs.py,sha256=J5U65Y3yOTqxDEZRCdrEmO63XQCeDzOJea1PwPM6Cyc,5146
@@ -210,7 +210,7 @@ dissect/target/plugins/os/unix/bsd/osx/user.py,sha256=qopB0s3n7e6Q7NjWzn8Z-dKtDt
210
210
  dissect/target/plugins/os/unix/esxi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
211
211
  dissect/target/plugins/os/unix/esxi/_os.py,sha256=s6pAgUyfHh3QcY6sgvk5uVMmLvqK1tIHWR7MSbrFn8w,17789
212
212
  dissect/target/plugins/os/unix/etc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
- dissect/target/plugins/os/unix/etc/etc.py,sha256=WNCtO7NWOKRDBiV-XjXqgPuGRDE_Zyw6XWz3kTm__QE,2493
213
+ dissect/target/plugins/os/unix/etc/etc.py,sha256=px_UwtPuk_scD-3nKJQZ0ao5lus9-BrSU4lPZWelYzI,2541
214
214
  dissect/target/plugins/os/unix/linux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
215
215
  dissect/target/plugins/os/unix/linux/_os.py,sha256=n6VkfGYIdZUxcK2C1aPDUY_ZZQEIl0GkrpvIKeguv5o,2812
216
216
  dissect/target/plugins/os/unix/linux/cmdline.py,sha256=AyMfndt3UsmJtoOyZYC8nWq2GZg9oPvn8SiI3M4NxnE,1622
@@ -326,14 +326,14 @@ dissect/target/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
326
326
  dissect/target/tools/build_pluginlist.py,sha256=5fomcuMwsVzcnYx5Htf5f9lSwsLeUUvomLUXNA4t7m4,849
327
327
  dissect/target/tools/dd.py,sha256=rTM-lgXxrYBpVAtJqFqAatDz45bLoD8-mFt_59Q3Lio,1928
328
328
  dissect/target/tools/fs.py,sha256=bdFSckOO-dyvvBpxOgPIx_UKGEbWGbOHF7kl6rWyt7U,6654
329
- dissect/target/tools/info.py,sha256=3smHr8I71yj3kCjsQ5nXkOHI9T_N8UwvkVa1CNOxB-s,5461
329
+ dissect/target/tools/info.py,sha256=SXU8_AXeFhw2XZBVQu3XW-ZDAewLvahI6Ag4TSq2-3A,5610
330
330
  dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
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
334
  dissect/target/tools/shell.py,sha256=_widEuIRqZhYzcFR52NYI8O2aPFm6tG5Uiv-AIrC32U,45155
335
335
  dissect/target/tools/utils.py,sha256=sQizexY3ui5vmWw4KOBLg5ecK3TPFjD-uxDqRn56ZTY,11304
336
- dissect/target/tools/yara.py,sha256=SZ0lKshWJ0TFTDUYONVKF04TgwmtDAttUPws9j9YSvk,1806
336
+ dissect/target/tools/yara.py,sha256=70k-2VMulf1EdkX03nCACzejaOEcsFHOyX-4E40MdQU,2044
337
337
  dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
338
338
  dissect/target/tools/dump/run.py,sha256=aD84peRS4zHqC78fH7Vd4ni3m1ZmVP70LyMwBRvoDGY,9463
339
339
  dissect/target/tools/dump/state.py,sha256=YYgCff0kZZ-tx27lJlc9LQ7AfoGnLK5Gyi796OnktA8,9205
@@ -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.dev30.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
350
- dissect.target-3.19.dev30.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
351
- dissect.target-3.19.dev30.dist-info/METADATA,sha256=fH2AsY4Du8s64GYdA4y4mJGfiEihIMfqkbOgE1ALNqc,12719
352
- dissect.target-3.19.dev30.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
353
- dissect.target-3.19.dev30.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
354
- dissect.target-3.19.dev30.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
355
- dissect.target-3.19.dev30.dist-info/RECORD,,
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,,