dissect.target 3.16.dev20__py3-none-any.whl → 3.16.dev21__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -120,9 +120,9 @@ class ConfigurationFilesystem(VirtualFilesystem):
120
120
  try:
121
121
  config_parser = parse(entry, *args, **kwargs)
122
122
  entry = ConfigurationEntry(self, entry.path, entry, config_parser)
123
- except ConfigurationParsingError:
123
+ except ConfigurationParsingError as e:
124
124
  # If a parsing error gets created, it should return the `entry`
125
- log.debug("Error when parsing %s", entry.path)
125
+ log.debug("Error when parsing %s with message '%s'", entry.path, e)
126
126
 
127
127
  return entry
128
128
 
@@ -220,13 +220,15 @@ class ConfigurationEntry(FilesystemEntry):
220
220
  output_buffer = io.StringIO()
221
221
 
222
222
  if isinstance(values, list):
223
- output_buffer.write(textwrap.indent(text="\n".join(values), prefix=prefix))
223
+ # Explicitly convert the list values to strings
224
+ _text = "\n".join(str(val) for val in values)
225
+ output_buffer.write(textwrap.indent(text=_text, prefix=prefix))
224
226
  elif hasattr(values, "keys"):
225
227
  for key, value in values.items():
226
228
  output_buffer.write(textwrap.indent(key, prefix=prefix) + "\n")
227
229
  output_buffer.write(self._write_value_mapping(value, indentation_nr + 4))
228
230
  else:
229
- output_buffer.write(textwrap.indent(values, prefix=prefix) + "\n")
231
+ output_buffer.write(textwrap.indent(str(values), prefix=prefix) + "\n")
230
232
 
231
233
  return output_buffer.getvalue()
232
234
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
+ import json
4
5
  import re
5
6
  from collections import deque
6
7
  from configparser import ConfigParser, MissingSectionHeaderError
@@ -26,6 +27,13 @@ from dissect.target.exceptions import ConfigurationParsingError, FileNotFoundErr
26
27
  from dissect.target.filesystem import FilesystemEntry
27
28
  from dissect.target.helpers.fsutil import TargetPath
28
29
 
30
+ try:
31
+ import yaml
32
+
33
+ PY_YAML = True
34
+ except (AttributeError, ImportError):
35
+ PY_YAML = False
36
+
29
37
 
30
38
  def _update_dictionary(current: dict[str, Any], key: str, value: Any) -> None:
31
39
  if prev_value := current.get(key):
@@ -151,7 +159,7 @@ class ConfigurationParser:
151
159
  try:
152
160
  self.parse_file(fh)
153
161
  except Exception as e:
154
- raise ConfigurationParsingError from e
162
+ raise ConfigurationParsingError(*e.args) from e
155
163
 
156
164
  if self.collapse_all or self.collapse:
157
165
  self.parsed_data = self._collapse_dict(self.parsed_data)
@@ -329,6 +337,77 @@ class Xml(ConfigurationParser):
329
337
  self.parsed_data = tree
330
338
 
331
339
 
340
+ class ListUnwrapper:
341
+ """Provides utility functions to unwrap dictionary objects out of lists."""
342
+
343
+ @staticmethod
344
+ def unwrap(data: Union[dict, list]) -> Union[dict, list]:
345
+ """Transforms a list with dictionaries to a dictionary.
346
+
347
+ The order of the list is preserved. If no dictionary is found,
348
+ the list remains untouched:
349
+
350
+ ["value1", "value2"] -> ["value1", "value2"]
351
+
352
+ {"data": "value"} -> {"data": "value"}
353
+
354
+ [{"data": "value"}] -> {
355
+ "list_item0": {
356
+ "data": "value"
357
+ }
358
+ }
359
+ """
360
+ orig = ListUnwrapper._unwrap_dict_list(data)
361
+ return ListUnwrapper._unwrap_dict(orig)
362
+
363
+ @staticmethod
364
+ def _unwrap_dict(data: Union[dict, list]) -> Union[dict, list]:
365
+ """Looks for dictionaries and unwraps its values."""
366
+
367
+ if not isinstance(data, dict):
368
+ return data
369
+
370
+ root = dict()
371
+ for key, value in data.items():
372
+ _value = ListUnwrapper._unwrap_dict_list(value)
373
+ if isinstance(_value, dict):
374
+ _value = ListUnwrapper._unwrap_dict(_value)
375
+ root[key] = _value
376
+
377
+ return root
378
+
379
+ @staticmethod
380
+ def _unwrap_dict_list(data: Union[dict, list]) -> Union[dict, list]:
381
+ """Unwraps a list containing dictionaries."""
382
+ if not isinstance(data, list) or not any(isinstance(obj, dict) for obj in data):
383
+ return data
384
+
385
+ return_value = {}
386
+ for idx, elem in enumerate(data):
387
+ return_value[f"list_item{idx}"] = elem
388
+
389
+ return return_value
390
+
391
+
392
+ class Json(ConfigurationParser):
393
+ """Parses a JSON file."""
394
+
395
+ def parse_file(self, fh: TextIO):
396
+ parsed_data = json.load(fh)
397
+ self.parsed_data = ListUnwrapper.unwrap(parsed_data)
398
+
399
+
400
+ class Yaml(ConfigurationParser):
401
+ """Parses a Yaml file."""
402
+
403
+ def parse_file(self, fh: TextIO) -> None:
404
+ if PY_YAML:
405
+ parsed_data = yaml.load(fh, yaml.BaseLoader)
406
+ self.parsed_data = ListUnwrapper.unwrap(parsed_data)
407
+ else:
408
+ raise ConfigurationParsingError("Failed to parse file, please install PyYAML.")
409
+
410
+
332
411
  class ScopeManager:
333
412
  """A (context)manager for dictionary scoping.
334
413
 
@@ -609,7 +688,9 @@ MATCH_MAP: dict[str, ParserConfig] = {
609
688
  CONFIG_MAP: dict[tuple[str, ...], ParserConfig] = {
610
689
  "ini": ParserConfig(Ini),
611
690
  "xml": ParserConfig(Xml),
612
- "json": ParserConfig(Txt),
691
+ "json": ParserConfig(Json),
692
+ "yml": ParserConfig(Yaml),
693
+ "yaml": ParserConfig(Yaml),
613
694
  "cnf": ParserConfig(Default),
614
695
  "conf": ParserConfig(Default, separator=(r"\s",)),
615
696
  "sample": ParserConfig(Txt),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.16.dev20
3
+ Version: 3.16.dev21
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
@@ -24,7 +24,7 @@ dissect/target/filesystems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
24
24
  dissect/target/filesystems/ad1.py,sha256=nEPzaaRsb6bL4ItFo0uLdmdLvrmK9BjqHeD3FOp3WQI,2413
25
25
  dissect/target/filesystems/btrfs.py,sha256=5MBi193ZvclkEQcxDr_sDHfj_FYU_hyYNRL4YqpDu4M,6243
26
26
  dissect/target/filesystems/cb.py,sha256=6LcoJiwsYu1Han31IUzVpZVDTifhTLTx_gLfNpB_p6k,5329
27
- dissect/target/filesystems/config.py,sha256=n1MR7a3tGXszpusLSDxZdTYo8IRLtDC_Xd95zPDTzzA,11295
27
+ dissect/target/filesystems/config.py,sha256=C2JnzBzMqbAjchGFDwURItCeUY7uxkhw1Gen-6cGkAc,11432
28
28
  dissect/target/filesystems/dir.py,sha256=7GRvojL151_Vk9e3vqgZbWE3I8IL9bU6LUKc_xjk6D4,4050
29
29
  dissect/target/filesystems/exfat.py,sha256=PRkZPUVN5NlgB1VetFtywdNgF6Yj5OBtF5a25t-fFvw,5917
30
30
  dissect/target/filesystems/extfs.py,sha256=9Cke-H0CL-SPd3-xvdAgfc3YA5hYso0sq6hm0C9vGII,4640
@@ -42,7 +42,7 @@ dissect/target/filesystems/zip.py,sha256=WT1bQhzX_1MXXVZTKrJniae4xqRqMZ8FsfbvhgG
42
42
  dissect/target/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  dissect/target/helpers/cache.py,sha256=_0w_iPD1OM066Ueyadb70erQW05jNnpJe-bDDN1UyXc,8444
44
44
  dissect/target/helpers/config.py,sha256=6917CZ6eDHaK_tOoiVEIndyhRXO6r6eCBIleq6f47PQ,2346
45
- dissect/target/helpers/configutil.py,sha256=u8pG_6dznwnQwj7JpSH54NcYwLAhFkgdyixoBVTDWM0,22587
45
+ dissect/target/helpers/configutil.py,sha256=t_UNvcWuMMT5C1tut_PgTwCnVUodf6RjhfXP4FSkmdo,25068
46
46
  dissect/target/helpers/cyber.py,sha256=Ki5oSU0GgQxjgC_yWoeieGP7GOY5blQCzNX7vy7Pgas,16782
47
47
  dissect/target/helpers/descriptor_extensions.py,sha256=uT8GwznfDAiIgMM7JKKOY0PXKMv2c0GCqJTCkWFgops,2605
48
48
  dissect/target/helpers/docs.py,sha256=J5U65Y3yOTqxDEZRCdrEmO63XQCeDzOJea1PwPM6Cyc,5146
@@ -323,10 +323,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
323
323
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
324
324
  dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
325
325
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
326
- dissect.target-3.16.dev20.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
327
- dissect.target-3.16.dev20.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
328
- dissect.target-3.16.dev20.dist-info/METADATA,sha256=nTJx6hfkv0OTGu8iDxjh3WG5KdZKEQExyc47vUkPjAM,11113
329
- dissect.target-3.16.dev20.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
330
- dissect.target-3.16.dev20.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
331
- dissect.target-3.16.dev20.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
332
- dissect.target-3.16.dev20.dist-info/RECORD,,
326
+ dissect.target-3.16.dev21.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
327
+ dissect.target-3.16.dev21.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
328
+ dissect.target-3.16.dev21.dist-info/METADATA,sha256=P5IeEWD-JvFiTMQeMulDT6W2H5J0Dvo8DbB5T1Cwyjg,11113
329
+ dissect.target-3.16.dev21.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
330
+ dissect.target-3.16.dev21.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
331
+ dissect.target-3.16.dev21.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
332
+ dissect.target-3.16.dev21.dist-info/RECORD,,