Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions dissect/target/helpers/configutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
from dissect.target.helpers.fsutil import TargetPath

try:
import yaml
from ruamel.yaml import YAML

PY_YAML = True
HAS_YAML = True
except ImportError:
PY_YAML = False
HAS_YAML = False

try:
if sys.version_info < (3, 11):
Expand Down Expand Up @@ -413,11 +413,11 @@ class Yaml(ConfigurationParser):
"""Parses a Yaml file."""

def parse_file(self, fh: TextIO) -> None:
if PY_YAML:
parsed_data = yaml.load(fh, yaml.BaseLoader)
if HAS_YAML:
parsed_data = YAML(typ="safe").load(fh)
self.parsed_data = ListUnwrapper.unwrap(parsed_data)
else:
raise ConfigurationParsingError("Failed to parse file, please install PyYAML.")
raise ConfigurationParsingError("Failed to parse file, please install ruamel.yaml.")


class Toml(ConfigurationParser):
Expand Down
43 changes: 22 additions & 21 deletions dissect/target/helpers/network_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
from dissect.target.target import Target

try:
import yaml
from ruamel.yaml import YAML

PY_YAML = True
HAS_YAML = True
except ImportError:
PY_YAML = False
HAS_YAML = False

IGNORED_IPS = [
"0.0.0.0",
Expand Down Expand Up @@ -49,7 +49,7 @@ def set_name(self, name: str) -> None:
"""Sets the name of the the used parsing template to the name of the discovered network manager."""
self.name = name

def create_config(self, path: TargetPath) -> Union[dict, None]:
def create_config(self, path: TargetPath) -> Optional[dict]:
"""Create a network config dictionary based on the configured template and supplied path.

Args:
Expand All @@ -73,26 +73,26 @@ def create_config(self, path: TargetPath) -> Union[dict, None]:
config = self._parse_configparser_config(path)
return config

def _parse_netplan_config(self, fh: TargetPath) -> Union[dict, None]:
def _parse_netplan_config(self, path: TargetPath) -> Optional[dict]:
"""Internal function to parse a netplan YAML based configuration file into a dict.

Args:
fh: A file-like object to the configuration file to be parsed.
fh: A path to the configuration file to be parsed.

Returns:
Dictionary containing the parsed YAML based configuration file.
"""
if PY_YAML:
return self.parser(stream=fh.open(), Loader=yaml.FullLoader)
if HAS_YAML:
return self.parser(path.open("rb"))
else:
self.target.log.error("Failed to parse %s. Cannot import PyYAML", self.name)
self.target.log.error("Failed to parse %s. Cannot import ruamel.yaml", self.name)
return None

def _parse_wicked_config(self, fh: TargetPath) -> dict:
def _parse_wicked_config(self, path: TargetPath) -> dict:
"""Internal function to parse a wicked XML based configuration file into a dict.

Args:
fh: A file-like object to the configuration file to be parsed.
fh: A path to the configuration file to be parsed.

Returns:
Dictionary containing the parsed xml based Linux network manager based configuration file.
Expand All @@ -101,44 +101,43 @@ def _parse_wicked_config(self, fh: TargetPath) -> dict:
# we have to replace the ":" for this with "___" (three underscores) to make the xml config non-namespaced.
pattern = compile(r"(?<=\n)\s+(<.+?>)")
replace_match: Callable[[Match]] = lambda match: match.group(1).replace(":", "___")
text = sub(pattern, replace_match, fh.open("rt").read())
text = sub(pattern, replace_match, path.open("rt").read())

xml = self.parser.parse(StringIO(text))
return self._parse_xml_config(xml, self.sections, self.options)

def _parse_configparser_config(self, fh: TargetPath) -> dict:
def _parse_configparser_config(self, path: TargetPath) -> dict:
"""Internal function to parse ConfigParser compatible configuration files into a dict.

Args:
fh: A file-like object to the configuration file to be parsed.
path: A path to the configuration file to be parsed.

Returns:
Dictionary containing the parsed ConfigParser compatible configuration file.
"""
try:
self.parser.read_string(fh.open("rt").read(), fh.name)
self.parser.read_string(path.open("rt").read(), path.name)
return self.parser._sections
except MissingSectionHeaderError:
# configparser does like config files without headers, so we inject a header to make it work.
self.parser.read_string(f"[{self.name}]\n" + fh.open("rt").read(), fh.name)
self.parser.read_string(f"[{self.name}]\n" + path.open("rt").read(), path.name)
return self.parser._sections

def _parse_text_config(self, comments: str, delim: str, fh: TargetPath) -> dict:
def _parse_text_config(self, comments: str, delim: str, path: TargetPath) -> dict:
"""Internal function to parse a basic plain text based configuration file into a dict.

Args:
comments: A string value defining the comment style of the configuration file.
delim: A string value defining the delimiters used in the configuration file.
fh: A file-like object to the configuration file to be parsed.
path: A path to the configuration file to be parsed.

Returns:
Dictionary with a parsed plain text based Linux network manager configuration file.
"""
config = defaultdict(dict)
option_dict = {}
fh = fh.open("rt")

for line in fh.readlines():
for line in path.open("rt"):
line = line.strip()

if not line or line.startswith(comments):
Expand Down Expand Up @@ -631,7 +630,9 @@ def should_ignore_ip(ip: str) -> bool:
["netctl"],
["address", "gateway", "dns", "ip"],
),
"netplan": Template("netplan", yaml.load if PY_YAML else None, ["network"], ["addresses", "dhcp4", "gateway4"]),
"netplan": Template(
"netplan", YAML(typ="safe").load if HAS_YAML else None, ["network"], ["addresses", "dhcp4", "gateway4"]
),
"NetworkManager": Template(
"NetworkManager",
ConfigParser(delimiters=("="), comment_prefixes="#", dict_type=dict),
Expand Down
21 changes: 12 additions & 9 deletions dissect/target/loaders/target.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
from pathlib import Path
from typing import TYPE_CHECKING

try:
import yaml
from ruamel.yaml import YAML
except ImportError:
raise ImportError("Missing PyYAML dependency")
raise ImportError("Missing ruamel.yaml dependency")

from dissect.target import container
from dissect.target.loader import Loader

if TYPE_CHECKING:
from dissect.target.target import Target


class TargetLoader(Loader):
"""Load target files."""

def __init__(self, path, **kwargs):
def __init__(self, path: Path, **kwargs):
super().__init__(path)
self.base_dir = path.parent
self.definition = yaml.safe_load(path.open("rb"))
self.definition = YAML(typ="safe").load(path.open("rb"))

@staticmethod
def detect(path):
def detect(path: Path) -> bool:
return path.suffix.lower() == ".target"

def map(self, target):
def map(self, target: Target) -> None:
for disk in self.definition["disks"]:
target.disks.add(container.open(disk))

def open(self, path):
return self.base_dir.joinpath(path).open("rb")
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ full = [
"ipython",
"fusepy",
"pycryptodome",
"pyyaml",
"ruamel.yaml",
"tomli; python_version<'3.11'",
# dissect.target's caching uses flow.record functionlity which depends on the
# zstandard module being available. However flow.record does not define
Expand Down