dissect.target 3.20.dev27__py3-none-any.whl → 3.20.dev28__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/helpers/configutil.py +74 -0
- dissect/target/plugins/apps/other/env.py +56 -0
- {dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/RECORD +9 -8
- {dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import io
|
4
4
|
import json
|
5
|
+
import logging
|
5
6
|
import re
|
6
7
|
import sys
|
7
8
|
from collections import deque
|
@@ -47,6 +48,9 @@ except ImportError:
|
|
47
48
|
HAS_TOML = False
|
48
49
|
|
49
50
|
|
51
|
+
log = logging.getLogger(__name__)
|
52
|
+
|
53
|
+
|
50
54
|
def _update_dictionary(current: dict[str, Any], key: str, value: Any) -> None:
|
51
55
|
if prev_value := current.get(key):
|
52
56
|
if isinstance(prev_value, dict):
|
@@ -465,6 +469,76 @@ class Toml(ConfigurationParser):
|
|
465
469
|
raise ConfigurationParsingError("Failed to parse file, please install tomli.")
|
466
470
|
|
467
471
|
|
472
|
+
class Env(ConfigurationParser):
|
473
|
+
"""Parses ``.env`` file contents according to Docker and bash specification.
|
474
|
+
|
475
|
+
Does not apply interpolation of substituted values, eg. ``foo=${bar}`` and does not attempt
|
476
|
+
to parse list or dict strings. Does not support dynamic env files, eg. `` foo=`bar` ``. Also
|
477
|
+
does not support multi-line key/value assignments (yet).
|
478
|
+
|
479
|
+
Resources:
|
480
|
+
- https://docs.docker.com/compose/environment-variables/variable-interpolation/#env-file-syntax
|
481
|
+
- https://github.com/theskumar/python-dotenv/blob/main/src/dotenv/parser.py
|
482
|
+
"""
|
483
|
+
|
484
|
+
RE_KV = re.compile(r"^(?P<key>.+?)=(?P<value>(\".+?\")|(\'.+?\')|(.*?))?(?P<comment> \#.+?)?$")
|
485
|
+
|
486
|
+
def __init__(self, comments: bool = True, *args, **kwargs) -> None:
|
487
|
+
super().__init__(*args, **kwargs)
|
488
|
+
self.comments = comments
|
489
|
+
self.parsed_data: dict | tuple[dict, str | None] = {}
|
490
|
+
|
491
|
+
def parse_file(self, fh: TextIO) -> None:
|
492
|
+
for line in fh.readlines():
|
493
|
+
# Blank lines are ignored.
|
494
|
+
# Lines beginning with ``#`` are processed as comments and ignored.
|
495
|
+
if not line or line[0] == "#" or "=" not in line:
|
496
|
+
continue
|
497
|
+
|
498
|
+
# Each line represents a key-value pair. Values can optionally be quoted.
|
499
|
+
# Inline comments for unquoted values must be preceded with a space.
|
500
|
+
# Value may be empty.
|
501
|
+
match = self.RE_KV.match(line)
|
502
|
+
|
503
|
+
# Line could be invalid
|
504
|
+
if not match:
|
505
|
+
log.warning("Could not parse line in %s: '%s'", fh, line)
|
506
|
+
continue
|
507
|
+
|
508
|
+
key = match.groupdict()["key"]
|
509
|
+
value = match.groupdict().get("value") or ""
|
510
|
+
value = value.strip()
|
511
|
+
comment = match.groupdict().get("comment")
|
512
|
+
comment = comment.replace(" # ", "", 1) if comment else None
|
513
|
+
|
514
|
+
# Surrounding whitespace characters are removed, unless quoted.
|
515
|
+
if value and ((value[0] == '"' and value[-1] == '"') or (value[0] == "'" and value[-1] == "'")):
|
516
|
+
is_quoted = True
|
517
|
+
value = value.strip("\"'")
|
518
|
+
else:
|
519
|
+
is_quoted = False
|
520
|
+
value = value.strip()
|
521
|
+
|
522
|
+
# Unquoted values may start with a quote if they are properly escaped.
|
523
|
+
if not is_quoted and value[:2] in ["\\'", '\\"']:
|
524
|
+
value = value[1:]
|
525
|
+
|
526
|
+
# Interpret boolean values
|
527
|
+
if value.lower() in ["1", "true"]:
|
528
|
+
value = True
|
529
|
+
elif value.lower() in ["0", "false"]:
|
530
|
+
value = False
|
531
|
+
|
532
|
+
# Interpret integer values
|
533
|
+
if isinstance(value, str) and re.match(r"^[0-9]{1,}$", value):
|
534
|
+
value = int(value)
|
535
|
+
|
536
|
+
if key.strip() in self.parsed_data:
|
537
|
+
log.warning("Duplicate environment key '%s' in file %s", key.strip(), fh)
|
538
|
+
|
539
|
+
self.parsed_data[key.strip()] = (value, comment) if self.comments else value
|
540
|
+
|
541
|
+
|
468
542
|
class ScopeManager:
|
469
543
|
"""A (context)manager for dictionary scoping.
|
470
544
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
from typing import Iterator
|
2
|
+
|
3
|
+
from dissect.target.helpers.configutil import Env
|
4
|
+
from dissect.target.helpers.record import TargetRecordDescriptor
|
5
|
+
from dissect.target.plugin import Plugin, arg, export
|
6
|
+
|
7
|
+
EnvironmentFileRecord = TargetRecordDescriptor(
|
8
|
+
"application/other/file/environment",
|
9
|
+
[
|
10
|
+
("datetime", "ts_mtime"),
|
11
|
+
("string", "key"),
|
12
|
+
("string", "value"),
|
13
|
+
("string", "comment"),
|
14
|
+
("path", "path"),
|
15
|
+
],
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
class EnvironmentFilePlugin(Plugin):
|
20
|
+
"""Environment file plugin."""
|
21
|
+
|
22
|
+
def check_compatible(self) -> None:
|
23
|
+
# `--env-path` is provided at runtime
|
24
|
+
pass
|
25
|
+
|
26
|
+
@export(record=EnvironmentFileRecord)
|
27
|
+
@arg("--env-path", help="path to scan environment files in")
|
28
|
+
@arg("--extension", help="extension of files to scan", default="env")
|
29
|
+
def envfile(self, env_path: str, extension: str = "env") -> Iterator[EnvironmentFileRecord]:
|
30
|
+
"""Yield environment variables found in ``.env`` files at the provided path."""
|
31
|
+
|
32
|
+
if not env_path:
|
33
|
+
self.target.log.error("No ``--path`` provided!")
|
34
|
+
|
35
|
+
if not (path := self.target.fs.path(env_path)).exists():
|
36
|
+
self.target.log.error("Provided path %s does not exist!", path)
|
37
|
+
|
38
|
+
for file in path.glob("**/*." + extension):
|
39
|
+
if not file.is_file():
|
40
|
+
continue
|
41
|
+
|
42
|
+
mtime = file.lstat().st_mtime
|
43
|
+
|
44
|
+
with file.open("r") as fh:
|
45
|
+
parser = Env(comments=True)
|
46
|
+
parser.read_file(fh)
|
47
|
+
|
48
|
+
for key, (value, comment) in parser.parsed_data.items():
|
49
|
+
yield EnvironmentFileRecord(
|
50
|
+
ts_mtime=mtime,
|
51
|
+
key=key,
|
52
|
+
value=value,
|
53
|
+
comment=comment,
|
54
|
+
path=file,
|
55
|
+
_target=self.target,
|
56
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev28
|
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
|
@@ -46,7 +46,7 @@ dissect/target/filesystems/zip.py,sha256=BeNj23DOYfWuTm5V1V419ViJiMfBrO1VA5gP6rl
|
|
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=RMHnIuKJHINHiLrvKN3EyA0jFA1o6-pbeaycG8Pgrp8,2596
|
49
|
-
dissect/target/helpers/configutil.py,sha256=
|
49
|
+
dissect/target/helpers/configutil.py,sha256=mO2XwhzLhGjFQzg_zC8SNi24CQhye1fhkYlH5Q5HFm8,31365
|
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
|
@@ -127,6 +127,7 @@ dissect/target/plugins/apps/browser/firefox.py,sha256=mZBBagFfIdiz9kUyK4Hi989I4g
|
|
127
127
|
dissect/target/plugins/apps/browser/iexplore.py,sha256=g_xw0toaiyjevxO8g9XPCOqc-CXZp39FVquRhPFGdTE,8801
|
128
128
|
dissect/target/plugins/apps/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
129
129
|
dissect/target/plugins/apps/container/docker.py,sha256=LTsZplaECSfO1Ysp_Y-9WsnNocsreu_iHO8fbSif3g0,16221
|
130
|
+
dissect/target/plugins/apps/other/env.py,sha256=_I12S_wjyT18WlUJ5cWOy5OTI140AheH6tq743iiyys,1874
|
130
131
|
dissect/target/plugins/apps/remoteaccess/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
131
132
|
dissect/target/plugins/apps/remoteaccess/anydesk.py,sha256=IdijK3F6ppaB_IgKL-xDljlEbb8l9S2U0xSWKqK9xRs,4294
|
132
133
|
dissect/target/plugins/apps/remoteaccess/remoteaccess.py,sha256=DWXkRDVUpFr1icK2fYwSXdZD204Xz0yRuO7rcJOwIwc,825
|
@@ -368,10 +369,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
368
369
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
369
370
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
370
371
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
371
|
-
dissect.target-3.20.
|
372
|
-
dissect.target-3.20.
|
373
|
-
dissect.target-3.20.
|
374
|
-
dissect.target-3.20.
|
375
|
-
dissect.target-3.20.
|
376
|
-
dissect.target-3.20.
|
377
|
-
dissect.target-3.20.
|
372
|
+
dissect.target-3.20.dev28.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
373
|
+
dissect.target-3.20.dev28.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
374
|
+
dissect.target-3.20.dev28.dist-info/METADATA,sha256=KaInzlw-bjxrdEkeFM3pb6BDAM8IBEP6atLJ3wjH_d4,12897
|
375
|
+
dissect.target-3.20.dev28.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
376
|
+
dissect.target-3.20.dev28.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
377
|
+
dissect.target-3.20.dev28.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
378
|
+
dissect.target-3.20.dev28.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.20.dev27.dist-info → dissect.target-3.20.dev28.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|