dissect.target 3.16.dev19__py3-none-any.whl → 3.16.dev21__py3-none-any.whl
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.
- dissect/target/filesystems/config.py +6 -4
- dissect/target/helpers/configutil.py +83 -2
- dissect/target/plugins/apps/webserver/apache.py +8 -1
- {dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/METADATA +1 -1
- {dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/RECORD +10 -10
- {dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/LICENSE +0 -0
- {dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/WHEEL +0 -0
- {dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
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(
|
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),
|
@@ -171,7 +171,14 @@ class ApachePlugin(WebserverPlugin):
|
|
171
171
|
|
172
172
|
__namespace__ = "apache"
|
173
173
|
|
174
|
-
DEFAULT_LOG_DIRS = [
|
174
|
+
DEFAULT_LOG_DIRS = [
|
175
|
+
"/var/log/apache2",
|
176
|
+
"/var/log/apache",
|
177
|
+
"/var/log/httpd",
|
178
|
+
"/var/log",
|
179
|
+
"sysvol/xampp/apache/logs",
|
180
|
+
"/opt/lampp/logs",
|
181
|
+
]
|
175
182
|
ACCESS_LOG_NAMES = ["access.log", "access_log", "httpd-access.log"]
|
176
183
|
ERROR_LOG_NAMES = ["error.log"]
|
177
184
|
DEFAULT_CONFIG_PATHS = [
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.16.
|
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=
|
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=
|
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
|
@@ -137,7 +137,7 @@ dissect/target/plugins/apps/vpn/wireguard.py,sha256=45WvCqQQGrG3DVDH5ghcsGpM_Bom
|
|
137
137
|
dissect/target/plugins/apps/webhosting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
138
138
|
dissect/target/plugins/apps/webhosting/cpanel.py,sha256=OeFQnu9GmpffIlFyK-AR2Qf8tjyMhazWEAUyccDU5y0,2979
|
139
139
|
dissect/target/plugins/apps/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
140
|
-
dissect/target/plugins/apps/webserver/apache.py,sha256=
|
140
|
+
dissect/target/plugins/apps/webserver/apache.py,sha256=bD6_XObfO7W-cFlLRWyhdwEfw-Y57JBi47rWnXCx-vg,15026
|
141
141
|
dissect/target/plugins/apps/webserver/caddy.py,sha256=qZsAK_tILGvroV4SWkDKc-Otwd41bUEtv9H9TuHmt-0,6422
|
142
142
|
dissect/target/plugins/apps/webserver/citrix.py,sha256=FEPdBteEJeeGg3B95W_27O9wLJVhenEc5A5fSLDmK18,3044
|
143
143
|
dissect/target/plugins/apps/webserver/iis.py,sha256=UwRVzLqnKScijdLoZFfpkSUzKTQosicZpn16q__4QBU,14669
|
@@ -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.
|
327
|
-
dissect.target-3.16.
|
328
|
-
dissect.target-3.16.
|
329
|
-
dissect.target-3.16.
|
330
|
-
dissect.target-3.16.
|
331
|
-
dissect.target-3.16.
|
332
|
-
dissect.target-3.16.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.16.dev19.dist-info → dissect.target-3.16.dev21.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|