dissect.target 3.20.dev27__py3-none-any.whl → 3.20.dev28__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.dev27
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=2PjQG-8vsParTANkMox_2cB1_MDxpOURJrUFj5dL3DI,28355
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.dev27.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
372
- dissect.target-3.20.dev27.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
373
- dissect.target-3.20.dev27.dist-info/METADATA,sha256=O7DsHBHZKJGXZ1bQFS5nypC1x3WA9342Xj88YwbbT4k,12897
374
- dissect.target-3.20.dev27.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
375
- dissect.target-3.20.dev27.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
376
- dissect.target-3.20.dev27.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
377
- dissect.target-3.20.dev27.dist-info/RECORD,,
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,,