From d4b682e937e5063dc8a45a5b9e20d22d7febb67c Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Fri, 5 Jan 2024 16:23:49 +0100
Subject: [PATCH 01/25] Add support for XML configuration files (DIS-2157)
---
dissect/target/helpers/configutil.py | 59 ++++++++++++++++++++++++-
tests/_data/helpers/configutil/test.xml | 17 +++++++
tests/filesystems/test_config.py | 58 ++++++++++++++++++++++++
3 files changed, 133 insertions(+), 1 deletion(-)
create mode 100644 tests/_data/helpers/configutil/test.xml
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 2d0ec4b34..217b139d6 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -20,6 +20,8 @@
Union,
)
+from defusedxml import ElementTree
+
from dissect.target.exceptions import ConfigurationParsingError, FileNotFoundError
from dissect.target.filesystem import FilesystemEntry
from dissect.target.helpers.fsutil import TargetPath
@@ -254,6 +256,59 @@ def parse_file(self, fh: TextIO) -> None:
self.parsed_data = {"content": fh.read(), "size": str(fh.tell())}
+class Xml(ConfigurationParser):
+ def _tree(self, tree: ElementTree) -> dict:
+ nodes = {}
+ counter = {}
+ for node in tree.findall("*"):
+ if node.tag in counter:
+ counter[node.tag] += 1
+ nodes[f"{node.tag}-{counter[node.tag]}"] = self._tree(node)
+ else:
+ counter[node.tag] = 1
+ nodes[f"{node.tag}"] = self._tree(node)
+ result = {"tag": tree.tag}
+ if tree.attrib:
+ result["attributes"] = tree.attrib
+ if nodes:
+ result["nodes"] = nodes
+
+ text = str(tree.text).strip(" \n\r")
+ if text:
+ result["text"] = text
+
+ return result
+
+ def _fix(self, content: str, position: tuple(int, int)) -> str:
+ """Quick heuritsic fix. If there is an invalid token, just remove it"""
+ lineno, offset = position
+ lines = content.split("\n")
+ line = lines[lineno - 1]
+ line = line[: offset - 1] + "" + line[offset + 1 :]
+ lines[lineno - 1] = line
+ return "\n".join(lines)
+
+ def parse_file(self, fh: TextIO) -> None:
+ content = fh.read()
+ document = content
+ errors = 0
+ limit = 20
+ tree = None
+ while not tree and errors < limit:
+ try:
+ tree = self._tree(ElementTree.fromstring(document))
+ break
+ except ElementTree.ParseError as err:
+ errors += 1
+ document = self._fix(document, err.position)
+
+ if not tree:
+ # Document could not be parsed, we give up
+ self.parsed_data = {"nodes": tree, "errors": errors, "text": content}
+ else:
+ self.parsed_data = {"nodes": tree, "errors": errors}
+
+
class ScopeManager:
"""A (context)manager for dictionary scoping.
@@ -528,11 +583,12 @@ def create_parser(self, options: Optional[ParserOptions] = None) -> Configuratio
"*/systemd/*": ParserConfig(SystemD),
"*/sysconfig/network-scripts/ifcfg-*": ParserConfig(Default),
"*/sysctl.d/*.conf": ParserConfig(Default),
+ "*/xml/*": ParserConfig(Xml),
}
CONFIG_MAP: dict[tuple[str, ...], ParserConfig] = {
"ini": ParserConfig(Ini),
- "xml": ParserConfig(Txt),
+ "xml": ParserConfig(Xml),
"json": ParserConfig(Txt),
"cnf": ParserConfig(Default),
"conf": ParserConfig(Default, separator=(r"\s",)),
@@ -549,6 +605,7 @@ def create_parser(self, options: Optional[ParserOptions] = None) -> Configuratio
"hosts": ParserConfig(Default, separator=(r"\s",)),
"nsswitch.conf": ParserConfig(Default, separator=(":",)),
"lsb-release": ParserConfig(Default),
+ "catalog": ParserConfig(Xml),
}
diff --git a/tests/_data/helpers/configutil/test.xml b/tests/_data/helpers/configutil/test.xml
new file mode 100644
index 000000000..00577232a
--- /dev/null
+++ b/tests/_data/helpers/configutil/test.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/filesystems/test_config.py b/tests/filesystems/test_config.py
index d201afefa..7ab647010 100644
--- a/tests/filesystems/test_config.py
+++ b/tests/filesystems/test_config.py
@@ -73,6 +73,64 @@ def mapped_file(test_file: str, fs_unix: VirtualFilesystem) -> VirtualFilesystem
},
},
),
+ (
+ "_data/helpers/configutil/test.xml",
+ {
+ "nodes": {
+ "tag": "Server",
+ "attributes": {"port": "8005", "shutdown": "SHUTDOWN"},
+ "nodes": {
+ "Listener": {
+ "tag": "Listener",
+ "attributes": {"className": "org.apache.catalina.core.JasperListener"},
+ },
+ "Service": {
+ "tag": "Service",
+ "attributes": {"name": "Catalina"},
+ "nodes": {
+ "Connector": {
+ "tag": "Connector",
+ "attributes": {
+ "port": "8080",
+ "protocol": "HTTP/1.1",
+ "connectionTimeout": "20000",
+ "redirectPort": "8443",
+ },
+ },
+ "Engine": {
+ "tag": "Engine",
+ "attributes": {"name": "Catalina", "defaultHost": "localhost"},
+ "nodes": {
+ "Host": {
+ "tag": "Host",
+ "attributes": {
+ "name": "localhost",
+ "appBase": "webapps",
+ "unpackWARs": "true",
+ "autoDeploy": "true",
+ },
+ "nodes": {
+ "Valve": {
+ "tag": "Valve",
+ "attributes": {
+ "className": "org.apache.catalina.valves.AccessLogValve",
+ "directory": "logs",
+ "prefix": "localhost_access_log.",
+ "suffix": ".txt",
+ "pattern": "%h %l %u %t s",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "errors": 3,
+ },
+ ),
],
)
def test_parse_file_input(target_unix: Target, mapped_file: str, expected_output: dict) -> None:
From b7f4402482bfd75bbb57e03fcc3d887c6f309ff9 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Fri, 5 Jan 2024 16:54:03 +0100
Subject: [PATCH 02/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Erik Schamper <1254028+Schamper@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 217b139d6..b094c10b1 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -280,7 +280,7 @@ def _tree(self, tree: ElementTree) -> dict:
return result
def _fix(self, content: str, position: tuple(int, int)) -> str:
- """Quick heuritsic fix. If there is an invalid token, just remove it"""
+ """Quick heuristic fix. If there is an invalid token, just remove it."""
lineno, offset = position
lines = content.split("\n")
line = lines[lineno - 1]
From 1ed21712cfd38b05c98166ba0a1e11b3eb9fe208 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:38:36 +0100
Subject: [PATCH 03/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 38 +++++++++++++++-------------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index b094c10b1..7fa5cc80d 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -258,26 +258,30 @@ def parse_file(self, fh: TextIO) -> None:
class Xml(ConfigurationParser):
def _tree(self, tree: ElementTree) -> dict:
- nodes = {}
- counter = {}
- for node in tree.findall("*"):
- if node.tag in counter:
- counter[node.tag] += 1
- nodes[f"{node.tag}-{counter[node.tag]}"] = self._tree(node)
- else:
- counter[node.tag] = 1
- nodes[f"{node.tag}"] = self._tree(node)
- result = {"tag": tree.tag}
+ root = {tree.tag: {} if tree.attrib else None}
+
+ children = list(tree)
+
+ if children:
+ childs = defaultdict(list)
+ for child in map(self._tree, children):
+ for key, value in child.items():
+ childs[key].append(value)
+
+ root = {tree.tag: {key: value[0] if len(value) == 1 else value for key, value in childs.items()}}
+
if tree.attrib:
- result["attributes"] = tree.attrib
- if nodes:
- result["nodes"] = nodes
+ root[tree.tag].update((key, value) for key, value in tree.attrib.items())
- text = str(tree.text).strip(" \n\r")
- if text:
- result["text"] = text
+ if tree.text:
+ text = tree.text.strip()
+ if children or tree.attrib:
+ if text:
+ root[tree.tag] = text
+ else:
+ root[tree.tag] = text
- return result
+ return root
def _fix(self, content: str, position: tuple(int, int)) -> str:
"""Quick heuristic fix. If there is an invalid token, just remove it."""
From 8d39839dd79fdc6fd8073d2f9de8d1203703d096 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:38:46 +0100
Subject: [PATCH 04/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 7fa5cc80d..9a8aa581f 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -297,7 +297,7 @@ def parse_file(self, fh: TextIO) -> None:
document = content
errors = 0
limit = 20
- tree = None
+ tree = {}
while not tree and errors < limit:
try:
tree = self._tree(ElementTree.fromstring(document))
From 351995d3cc61babccb9776cba60123e1cebcdd99 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:39:16 +0100
Subject: [PATCH 05/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 9a8aa581f..8303c517a 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -308,9 +308,11 @@ def parse_file(self, fh: TextIO) -> None:
if not tree:
# Document could not be parsed, we give up
- self.parsed_data = {"nodes": tree, "errors": errors, "text": content}
- else:
- self.parsed_data = {"nodes": tree, "errors": errors}
+ if not tree:
+ # Document could not be parsed, we give up
+ raise ConfigurationParsingError(f"Could not parse XML file: {fh.name} after {errors} attempts.")
+
+ self.parsed_data = {**tree, "errors": errors}
class ScopeManager:
From 53f22d860ca9c03177ce06ed58b86e61518fb93d Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Fri, 5 Jan 2024 16:23:49 +0100
Subject: [PATCH 06/25] Add support for XML configuration files (DIS-2157)
---
dissect/target/helpers/configutil.py | 59 ++++++++++++++++++++++++-
tests/_data/helpers/configutil/test.xml | 17 +++++++
tests/filesystems/test_config.py | 58 ++++++++++++++++++++++++
3 files changed, 133 insertions(+), 1 deletion(-)
create mode 100644 tests/_data/helpers/configutil/test.xml
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 2d0ec4b34..217b139d6 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -20,6 +20,8 @@
Union,
)
+from defusedxml import ElementTree
+
from dissect.target.exceptions import ConfigurationParsingError, FileNotFoundError
from dissect.target.filesystem import FilesystemEntry
from dissect.target.helpers.fsutil import TargetPath
@@ -254,6 +256,59 @@ def parse_file(self, fh: TextIO) -> None:
self.parsed_data = {"content": fh.read(), "size": str(fh.tell())}
+class Xml(ConfigurationParser):
+ def _tree(self, tree: ElementTree) -> dict:
+ nodes = {}
+ counter = {}
+ for node in tree.findall("*"):
+ if node.tag in counter:
+ counter[node.tag] += 1
+ nodes[f"{node.tag}-{counter[node.tag]}"] = self._tree(node)
+ else:
+ counter[node.tag] = 1
+ nodes[f"{node.tag}"] = self._tree(node)
+ result = {"tag": tree.tag}
+ if tree.attrib:
+ result["attributes"] = tree.attrib
+ if nodes:
+ result["nodes"] = nodes
+
+ text = str(tree.text).strip(" \n\r")
+ if text:
+ result["text"] = text
+
+ return result
+
+ def _fix(self, content: str, position: tuple(int, int)) -> str:
+ """Quick heuritsic fix. If there is an invalid token, just remove it"""
+ lineno, offset = position
+ lines = content.split("\n")
+ line = lines[lineno - 1]
+ line = line[: offset - 1] + "" + line[offset + 1 :]
+ lines[lineno - 1] = line
+ return "\n".join(lines)
+
+ def parse_file(self, fh: TextIO) -> None:
+ content = fh.read()
+ document = content
+ errors = 0
+ limit = 20
+ tree = None
+ while not tree and errors < limit:
+ try:
+ tree = self._tree(ElementTree.fromstring(document))
+ break
+ except ElementTree.ParseError as err:
+ errors += 1
+ document = self._fix(document, err.position)
+
+ if not tree:
+ # Document could not be parsed, we give up
+ self.parsed_data = {"nodes": tree, "errors": errors, "text": content}
+ else:
+ self.parsed_data = {"nodes": tree, "errors": errors}
+
+
class ScopeManager:
"""A (context)manager for dictionary scoping.
@@ -528,11 +583,12 @@ def create_parser(self, options: Optional[ParserOptions] = None) -> Configuratio
"*/systemd/*": ParserConfig(SystemD),
"*/sysconfig/network-scripts/ifcfg-*": ParserConfig(Default),
"*/sysctl.d/*.conf": ParserConfig(Default),
+ "*/xml/*": ParserConfig(Xml),
}
CONFIG_MAP: dict[tuple[str, ...], ParserConfig] = {
"ini": ParserConfig(Ini),
- "xml": ParserConfig(Txt),
+ "xml": ParserConfig(Xml),
"json": ParserConfig(Txt),
"cnf": ParserConfig(Default),
"conf": ParserConfig(Default, separator=(r"\s",)),
@@ -549,6 +605,7 @@ def create_parser(self, options: Optional[ParserOptions] = None) -> Configuratio
"hosts": ParserConfig(Default, separator=(r"\s",)),
"nsswitch.conf": ParserConfig(Default, separator=(":",)),
"lsb-release": ParserConfig(Default),
+ "catalog": ParserConfig(Xml),
}
diff --git a/tests/_data/helpers/configutil/test.xml b/tests/_data/helpers/configutil/test.xml
new file mode 100644
index 000000000..00577232a
--- /dev/null
+++ b/tests/_data/helpers/configutil/test.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/filesystems/test_config.py b/tests/filesystems/test_config.py
index d201afefa..7ab647010 100644
--- a/tests/filesystems/test_config.py
+++ b/tests/filesystems/test_config.py
@@ -73,6 +73,64 @@ def mapped_file(test_file: str, fs_unix: VirtualFilesystem) -> VirtualFilesystem
},
},
),
+ (
+ "_data/helpers/configutil/test.xml",
+ {
+ "nodes": {
+ "tag": "Server",
+ "attributes": {"port": "8005", "shutdown": "SHUTDOWN"},
+ "nodes": {
+ "Listener": {
+ "tag": "Listener",
+ "attributes": {"className": "org.apache.catalina.core.JasperListener"},
+ },
+ "Service": {
+ "tag": "Service",
+ "attributes": {"name": "Catalina"},
+ "nodes": {
+ "Connector": {
+ "tag": "Connector",
+ "attributes": {
+ "port": "8080",
+ "protocol": "HTTP/1.1",
+ "connectionTimeout": "20000",
+ "redirectPort": "8443",
+ },
+ },
+ "Engine": {
+ "tag": "Engine",
+ "attributes": {"name": "Catalina", "defaultHost": "localhost"},
+ "nodes": {
+ "Host": {
+ "tag": "Host",
+ "attributes": {
+ "name": "localhost",
+ "appBase": "webapps",
+ "unpackWARs": "true",
+ "autoDeploy": "true",
+ },
+ "nodes": {
+ "Valve": {
+ "tag": "Valve",
+ "attributes": {
+ "className": "org.apache.catalina.valves.AccessLogValve",
+ "directory": "logs",
+ "prefix": "localhost_access_log.",
+ "suffix": ".txt",
+ "pattern": "%h %l %u %t s",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "errors": 3,
+ },
+ ),
],
)
def test_parse_file_input(target_unix: Target, mapped_file: str, expected_output: dict) -> None:
From 1aecf767e929e105301bf84f59e0ac75aa3a23ac Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Fri, 5 Jan 2024 16:54:03 +0100
Subject: [PATCH 07/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Erik Schamper <1254028+Schamper@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 217b139d6..b094c10b1 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -280,7 +280,7 @@ def _tree(self, tree: ElementTree) -> dict:
return result
def _fix(self, content: str, position: tuple(int, int)) -> str:
- """Quick heuritsic fix. If there is an invalid token, just remove it"""
+ """Quick heuristic fix. If there is an invalid token, just remove it."""
lineno, offset = position
lines = content.split("\n")
line = lines[lineno - 1]
From 7411820e53e157aca4335c9664ba4b9e9db6588e Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:38:36 +0100
Subject: [PATCH 08/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 38 +++++++++++++++-------------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index b094c10b1..7fa5cc80d 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -258,26 +258,30 @@ def parse_file(self, fh: TextIO) -> None:
class Xml(ConfigurationParser):
def _tree(self, tree: ElementTree) -> dict:
- nodes = {}
- counter = {}
- for node in tree.findall("*"):
- if node.tag in counter:
- counter[node.tag] += 1
- nodes[f"{node.tag}-{counter[node.tag]}"] = self._tree(node)
- else:
- counter[node.tag] = 1
- nodes[f"{node.tag}"] = self._tree(node)
- result = {"tag": tree.tag}
+ root = {tree.tag: {} if tree.attrib else None}
+
+ children = list(tree)
+
+ if children:
+ childs = defaultdict(list)
+ for child in map(self._tree, children):
+ for key, value in child.items():
+ childs[key].append(value)
+
+ root = {tree.tag: {key: value[0] if len(value) == 1 else value for key, value in childs.items()}}
+
if tree.attrib:
- result["attributes"] = tree.attrib
- if nodes:
- result["nodes"] = nodes
+ root[tree.tag].update((key, value) for key, value in tree.attrib.items())
- text = str(tree.text).strip(" \n\r")
- if text:
- result["text"] = text
+ if tree.text:
+ text = tree.text.strip()
+ if children or tree.attrib:
+ if text:
+ root[tree.tag] = text
+ else:
+ root[tree.tag] = text
- return result
+ return root
def _fix(self, content: str, position: tuple(int, int)) -> str:
"""Quick heuristic fix. If there is an invalid token, just remove it."""
From 8dbe3d69ce51c39bb40406aed7971e6888b7f150 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:38:46 +0100
Subject: [PATCH 09/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 7fa5cc80d..9a8aa581f 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -297,7 +297,7 @@ def parse_file(self, fh: TextIO) -> None:
document = content
errors = 0
limit = 20
- tree = None
+ tree = {}
while not tree and errors < limit:
try:
tree = self._tree(ElementTree.fromstring(document))
From 2e7a990975c4f29163cb4f75abdc75703d9d8ca9 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:39:16 +0100
Subject: [PATCH 10/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 9a8aa581f..8303c517a 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -308,9 +308,11 @@ def parse_file(self, fh: TextIO) -> None:
if not tree:
# Document could not be parsed, we give up
- self.parsed_data = {"nodes": tree, "errors": errors, "text": content}
- else:
- self.parsed_data = {"nodes": tree, "errors": errors}
+ if not tree:
+ # Document could not be parsed, we give up
+ raise ConfigurationParsingError(f"Could not parse XML file: {fh.name} after {errors} attempts.")
+
+ self.parsed_data = {**tree, "errors": errors}
class ScopeManager:
From 601c79a10cb7c7cee3a44e577ce6dfe460ddf1a4 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 12:33:49 +0100
Subject: [PATCH 11/25] Integrate feedback.
---
dissect/target/helpers/configutil.py | 6 +-
dissect/target/helpers/mount.py | 3 +-
dissect/target/tools/mount.py | 3 +-
tests/_data/helpers/configutil/test.xml | 3 +-
tests/filesystems/test_config.py | 74 +++++++++----------------
5 files changed, 33 insertions(+), 56 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 8303c517a..0d6592efc 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -2,7 +2,7 @@
import io
import re
-from collections import deque
+from collections import defaultdict, deque
from configparser import ConfigParser, MissingSectionHeaderError
from dataclasses import dataclass
from fnmatch import fnmatch
@@ -257,6 +257,8 @@ def parse_file(self, fh: TextIO) -> None:
class Xml(ConfigurationParser):
+ """Parses XML-files, ignores any constructor parameters."""
+
def _tree(self, tree: ElementTree) -> dict:
root = {tree.tag: {} if tree.attrib else None}
@@ -306,8 +308,6 @@ def parse_file(self, fh: TextIO) -> None:
errors += 1
document = self._fix(document, err.position)
- if not tree:
- # Document could not be parsed, we give up
if not tree:
# Document could not be parsed, we give up
raise ConfigurationParsingError(f"Could not parse XML file: {fh.name} after {errors} attempts.")
diff --git a/dissect/target/helpers/mount.py b/dissect/target/helpers/mount.py
index 1f81b1627..060285b9f 100644
--- a/dissect/target/helpers/mount.py
+++ b/dissect/target/helpers/mount.py
@@ -3,9 +3,8 @@
from functools import lru_cache
from typing import BinaryIO, Optional
-from fuse import FuseOSError, Operations
-
from dissect.target.filesystem import Filesystem, FilesystemEntry
+from fuse import FuseOSError, Operations
log = logging.getLogger(__name__)
diff --git a/dissect/target/tools/mount.py b/dissect/target/tools/mount.py
index 7e4c31ac3..99efd29b0 100644
--- a/dissect/target/tools/mount.py
+++ b/dissect/target/tools/mount.py
@@ -12,9 +12,8 @@
)
try:
- from fuse import FUSE
-
from dissect.target.helpers.mount import DissectMount
+ from fuse import FUSE
HAS_FUSE = True
except Exception:
diff --git a/tests/_data/helpers/configutil/test.xml b/tests/_data/helpers/configutil/test.xml
index 00577232a..15500dea4 100644
--- a/tests/_data/helpers/configutil/test.xml
+++ b/tests/_data/helpers/configutil/test.xml
@@ -1,6 +1,7 @@
-
+ a
+ b
VirtualFilesystem
(
"_data/helpers/configutil/test.xml",
{
- "nodes": {
- "tag": "Server",
- "attributes": {"port": "8005", "shutdown": "SHUTDOWN"},
- "nodes": {
- "Listener": {
- "tag": "Listener",
- "attributes": {"className": "org.apache.catalina.core.JasperListener"},
+ "Server": {
+ "Listener": ["a", "b"], # @todo add attributes on list items
+ "Service": {
+ "Connector": {
+ "port": "8080",
+ "protocol": "HTTP/1.1",
+ "connectionTimeout": "20000",
+ "redirectPort": "8443",
},
- "Service": {
- "tag": "Service",
- "attributes": {"name": "Catalina"},
- "nodes": {
- "Connector": {
- "tag": "Connector",
- "attributes": {
- "port": "8080",
- "protocol": "HTTP/1.1",
- "connectionTimeout": "20000",
- "redirectPort": "8443",
- },
- },
- "Engine": {
- "tag": "Engine",
- "attributes": {"name": "Catalina", "defaultHost": "localhost"},
- "nodes": {
- "Host": {
- "tag": "Host",
- "attributes": {
- "name": "localhost",
- "appBase": "webapps",
- "unpackWARs": "true",
- "autoDeploy": "true",
- },
- "nodes": {
- "Valve": {
- "tag": "Valve",
- "attributes": {
- "className": "org.apache.catalina.valves.AccessLogValve",
- "directory": "logs",
- "prefix": "localhost_access_log.",
- "suffix": ".txt",
- "pattern": "%h %l %u %t s",
- },
- },
- },
- },
- },
+ "Engine": {
+ "Host": {
+ "Valve": {
+ "className": "org.apache.catalina.valves.AccessLogValve",
+ "directory": "logs",
+ "prefix": "localhost_access_log.",
+ "suffix": ".txt",
+ "pattern": "%h %l %u %t s",
},
+ "name": "localhost",
+ "appBase": "webapps",
+ "unpackWARs": "true",
+ "autoDeploy": "true",
},
+ "name": "Catalina",
+ "defaultHost": "localhost",
},
+ "name": "Catalina",
},
- },
- "errors": 3,
+ "port": "8005",
+ "shutdown": "SHUTDOWN",
+ }
},
),
],
From 20df2645bb082ea210ad89c43a2c8a2dcc8d7c9f Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 13:19:34 +0100
Subject: [PATCH 12/25] Integrate feedback.
---
tests/filesystems/test_config.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/filesystems/test_config.py b/tests/filesystems/test_config.py
index 1a20f1578..c4d5c0f10 100644
--- a/tests/filesystems/test_config.py
+++ b/tests/filesystems/test_config.py
@@ -77,7 +77,7 @@ def mapped_file(test_file: str, fs_unix: VirtualFilesystem) -> VirtualFilesystem
"_data/helpers/configutil/test.xml",
{
"Server": {
- "Listener": ["a", "b"], # @todo add attributes on list items
+ "Listener": ["a", "b"], # @todo add attributes on list items
"Service": {
"Connector": {
"port": "8080",
From c9aee01ba0c0c3216d84c4af0250f87a78274555 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 13:27:43 +0100
Subject: [PATCH 13/25] Integrate feedback.
---
dissect/target/helpers/mount.py | 3 ++-
dissect/target/tools/mount.py | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dissect/target/helpers/mount.py b/dissect/target/helpers/mount.py
index 060285b9f..1f81b1627 100644
--- a/dissect/target/helpers/mount.py
+++ b/dissect/target/helpers/mount.py
@@ -3,9 +3,10 @@
from functools import lru_cache
from typing import BinaryIO, Optional
-from dissect.target.filesystem import Filesystem, FilesystemEntry
from fuse import FuseOSError, Operations
+from dissect.target.filesystem import Filesystem, FilesystemEntry
+
log = logging.getLogger(__name__)
CACHE_SIZE = 1024 * 1024
diff --git a/dissect/target/tools/mount.py b/dissect/target/tools/mount.py
index 99efd29b0..7e4c31ac3 100644
--- a/dissect/target/tools/mount.py
+++ b/dissect/target/tools/mount.py
@@ -12,9 +12,10 @@
)
try:
- from dissect.target.helpers.mount import DissectMount
from fuse import FUSE
+ from dissect.target.helpers.mount import DissectMount
+
HAS_FUSE = True
except Exception:
HAS_FUSE = False
From fb830d66b0a2e33111a3e8695c259b7df5e44149 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Tue, 16 Jan 2024 13:40:48 +0100
Subject: [PATCH 14/25] Integrate feedback.
---
tests/plugins/general/test_config.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/plugins/general/test_config.py b/tests/plugins/general/test_config.py
index 0954da08b..d5cf0d63c 100644
--- a/tests/plugins/general/test_config.py
+++ b/tests/plugins/general/test_config.py
@@ -68,7 +68,7 @@ def test_collapse_types(
"hint, data_bytes",
[
("ini", b"[DEFAULT]\nkey=value"),
- ("xml", b"currently_just_text"),
+ ("xml", b"currently_just_text"),
("json", b"currently_just_text"),
("cnf", b"key=value"),
("conf", b"key value"),
From 2931f50da755f9b03b3b276edcc4981e654d09d4 Mon Sep 17 00:00:00 2001
From: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
Date: Thu, 18 Jan 2024 09:39:06 +0100
Subject: [PATCH 15/25] Minor parser & test changes
---
dissect/target/helpers/configutil.py | 17 +++++++++++++----
tests/filesystems/test_config.py | 5 ++++-
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 0d6592efc..87ffd9a68 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -257,7 +257,7 @@ def parse_file(self, fh: TextIO) -> None:
class Xml(ConfigurationParser):
- """Parses XML-files, ignores any constructor parameters."""
+ """Parses an XML file. Ignores any constructor parameters passed from ``ConfigurationParser`."""
def _tree(self, tree: ElementTree) -> dict:
root = {tree.tag: {} if tree.attrib else None}
@@ -279,7 +279,9 @@ def _tree(self, tree: ElementTree) -> dict:
text = tree.text.strip()
if children or tree.attrib:
if text:
- root[tree.tag] = text
+ # Case where a xml tag has an attribute and text value. E.g. a
+ # We add a sentinel value, which we can use to unpack later.
+ root[tree.tag]["$element_text"] = text
else:
root[tree.tag] = text
@@ -289,9 +291,12 @@ def _fix(self, content: str, position: tuple(int, int)) -> str:
"""Quick heuristic fix. If there is an invalid token, just remove it."""
lineno, offset = position
lines = content.split("\n")
+
line = lines[lineno - 1]
line = line[: offset - 1] + "" + line[offset + 1 :]
+
lines[lineno - 1] = line
+
return "\n".join(lines)
def parse_file(self, fh: TextIO) -> None:
@@ -300,19 +305,23 @@ def parse_file(self, fh: TextIO) -> None:
errors = 0
limit = 20
tree = {}
+
while not tree and errors < limit:
try:
tree = self._tree(ElementTree.fromstring(document))
+ import ipdb
+
+ ipdb.set_trace()
break
except ElementTree.ParseError as err:
errors += 1
document = self._fix(document, err.position)
if not tree:
- # Document could not be parsed, we give up
+ # Error limit reached. Thus we consider the document not parseable.
raise ConfigurationParsingError(f"Could not parse XML file: {fh.name} after {errors} attempts.")
- self.parsed_data = {**tree, "errors": errors}
+ self.parsed_data = tree
class ScopeManager:
diff --git a/tests/filesystems/test_config.py b/tests/filesystems/test_config.py
index c4d5c0f10..693c3f086 100644
--- a/tests/filesystems/test_config.py
+++ b/tests/filesystems/test_config.py
@@ -77,7 +77,10 @@ def mapped_file(test_file: str, fs_unix: VirtualFilesystem) -> VirtualFilesystem
"_data/helpers/configutil/test.xml",
{
"Server": {
- "Listener": ["a", "b"], # @todo add attributes on list items
+ "Listener": [
+ {"className": "org.apache.catalina.core.JasperListener1", "$element_text": "a"},
+ {"className": "org.apache.catalina.core.JasperListener2", "$element_text": "b"},
+ ],
"Service": {
"Connector": {
"port": "8080",
From d05e1cdf7c56fb344885db3411c3da9638e42993 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 18 Jan 2024 11:33:48 +0100
Subject: [PATCH 16/25] Remove debug code.
---
dissect/target/helpers/configutil.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 87ffd9a68..90a2e2311 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -309,9 +309,6 @@ def parse_file(self, fh: TextIO) -> None:
while not tree and errors < limit:
try:
tree = self._tree(ElementTree.fromstring(document))
- import ipdb
-
- ipdb.set_trace()
break
except ElementTree.ParseError as err:
errors += 1
From fec51f4f6cc79d839fb6a4f327e73f6f2d8f93bd Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 18 Jan 2024 16:41:03 +0100
Subject: [PATCH 17/25] Restore previous solution but with comments to explain
rationale.
---
dissect/target/helpers/configutil.py | 52 ++++++++++--------
tests/filesystems/test_config.py | 80 ++++++++++++++++++----------
2 files changed, 82 insertions(+), 50 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 90a2e2311..a36549cff 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -2,7 +2,7 @@
import io
import re
-from collections import defaultdict, deque
+from collections import deque
from configparser import ConfigParser, MissingSectionHeaderError
from dataclasses import dataclass
from fnmatch import fnmatch
@@ -260,32 +260,42 @@ class Xml(ConfigurationParser):
"""Parses an XML file. Ignores any constructor parameters passed from ``ConfigurationParser`."""
def _tree(self, tree: ElementTree) -> dict:
- root = {tree.tag: {} if tree.attrib else None}
-
- children = list(tree)
-
- if children:
- childs = defaultdict(list)
- for child in map(self._tree, children):
- for key, value in child.items():
- childs[key].append(value)
+ """Very simple but robust xml -> dict implementation, see comments."""
+ nodes = {}
+ counter = {}
+
+ # each node is a folder (so the structure is always the same! [1])
+ for node in tree.findall("*"):
+ # if a node contains multiple nodes with the same name, number them
+ if node.tag in counter:
+ counter[node.tag] += 1
+ nodes[f"{node.tag}-{counter[node.tag]}"] = self._tree(node)
+ else:
+ counter[node.tag] = 1
+ nodes[f"{node.tag}"] = self._tree(node)
- root = {tree.tag: {key: value[0] if len(value) == 1 else value for key, value in childs.items()}}
+ result = {"tag": tree.tag}
+ # all attribs go in the attribute folder
+ # (i.e. stable, does not change depending on xml structure! [2]
+ # Also, this way we "know" they have been attributes, i.e. we don't lose information! [3]
if tree.attrib:
- root[tree.tag].update((key, value) for key, value in tree.attrib.items())
+ result["attributes"] = tree.attrib
+ # all subnodes go in the nodes folder
+ if nodes:
+ result["nodes"] = nodes
+
+ # content goes into the text folder
+ # we don't use special prefixes ($) because XML docs may use them anyway (even though they are forbidden)
if tree.text:
- text = tree.text.strip()
- if children or tree.attrib:
- if text:
- # Case where a xml tag has an attribute and text value. E.g. a
- # We add a sentinel value, which we can use to unpack later.
- root[tree.tag]["$element_text"] = text
- else:
- root[tree.tag] = text
+ text = str(tree.text).strip(" \n\r")
+ if text != "":
+ result["text"] = text
+
+ # if you need to store meta-data, you can extend add more entries here... CDATA, Comments, errors
- return root
+ return result
def _fix(self, content: str, position: tuple(int, int)) -> str:
"""Quick heuristic fix. If there is an invalid token, just remove it."""
diff --git a/tests/filesystems/test_config.py b/tests/filesystems/test_config.py
index 693c3f086..c9d7dd830 100644
--- a/tests/filesystems/test_config.py
+++ b/tests/filesystems/test_config.py
@@ -76,40 +76,62 @@ def mapped_file(test_file: str, fs_unix: VirtualFilesystem) -> VirtualFilesystem
(
"_data/helpers/configutil/test.xml",
{
- "Server": {
- "Listener": [
- {"className": "org.apache.catalina.core.JasperListener1", "$element_text": "a"},
- {"className": "org.apache.catalina.core.JasperListener2", "$element_text": "b"},
- ],
+ "tag": "Server",
+ "attributes": {"port": "8005", "shutdown": "SHUTDOWN"},
+ "nodes": {
+ "Listener": {
+ "tag": "Listener",
+ "attributes": {"className": "org.apache.catalina.core.JasperListener1"},
+ "text": "a",
+ },
+ "Listener-2": {
+ "tag": "Listener",
+ "attributes": {"className": "org.apache.catalina.core.JasperListener2"},
+ "text": "b",
+ },
"Service": {
- "Connector": {
- "port": "8080",
- "protocol": "HTTP/1.1",
- "connectionTimeout": "20000",
- "redirectPort": "8443",
- },
- "Engine": {
- "Host": {
- "Valve": {
- "className": "org.apache.catalina.valves.AccessLogValve",
- "directory": "logs",
- "prefix": "localhost_access_log.",
- "suffix": ".txt",
- "pattern": "%h %l %u %t s",
+ "tag": "Service",
+ "attributes": {"name": "Catalina"},
+ "nodes": {
+ "Connector": {
+ "tag": "Connector",
+ "attributes": {
+ "port": "8080",
+ "protocol": "HTTP/1.1",
+ "connectionTimeout": "20000",
+ "redirectPort": "8443",
+ },
+ },
+ "Engine": {
+ "tag": "Engine",
+ "attributes": {"name": "Catalina", "defaultHost": "localhost"},
+ "nodes": {
+ "Host": {
+ "tag": "Host",
+ "attributes": {
+ "name": "localhost",
+ "appBase": "webapps",
+ "unpackWARs": "true",
+ "autoDeploy": "true",
+ },
+ "nodes": {
+ "Valve": {
+ "tag": "Valve",
+ "attributes": {
+ "className": "org.apache.catalina.valves.AccessLogValve",
+ "directory": "logs",
+ "prefix": "localhost_access_log.",
+ "suffix": ".txt",
+ "pattern": "%h %l %u %t s",
+ },
+ }
+ },
+ }
},
- "name": "localhost",
- "appBase": "webapps",
- "unpackWARs": "true",
- "autoDeploy": "true",
},
- "name": "Catalina",
- "defaultHost": "localhost",
},
- "name": "Catalina",
},
- "port": "8005",
- "shutdown": "SHUTDOWN",
- }
+ },
},
),
],
From e4e1f4f5de70e39102326362c26222836b393444 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:10:47 +0100
Subject: [PATCH 18/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index a36549cff..4a7f3840e 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -289,8 +289,7 @@ def _tree(self, tree: ElementTree) -> dict:
# content goes into the text folder
# we don't use special prefixes ($) because XML docs may use them anyway (even though they are forbidden)
if tree.text:
- text = str(tree.text).strip(" \n\r")
- if text != "":
+ if text := tree.text.strip(" \n\r"):
result["text"] = text
# if you need to store meta-data, you can extend add more entries here... CDATA, Comments, errors
From 07004e30e01a8b8788782bc986d490ee0065a721 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:12:25 +0100
Subject: [PATCH 19/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 4a7f3840e..fe2f23b55 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -272,7 +272,7 @@ def _tree(self, tree: ElementTree) -> dict:
nodes[f"{node.tag}-{counter[node.tag]}"] = self._tree(node)
else:
counter[node.tag] = 1
- nodes[f"{node.tag}"] = self._tree(node)
+ nodes[node.tag] = self._tree(node)
result = {"tag": tree.tag}
From 7a29b97a3daf303671c57d13f299dabdfe753fd3 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:14:19 +0100
Subject: [PATCH 20/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index fe2f23b55..42b7e638c 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -259,7 +259,7 @@ def parse_file(self, fh: TextIO) -> None:
class Xml(ConfigurationParser):
"""Parses an XML file. Ignores any constructor parameters passed from ``ConfigurationParser`."""
- def _tree(self, tree: ElementTree) -> dict:
+ def _tree(self, tree: ElementTree, root: bool = False) -> dict:
"""Very simple but robust xml -> dict implementation, see comments."""
nodes = {}
counter = {}
From 81e583d173a0a0594dd62f233372e6f5f53a98d2 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:14:29 +0100
Subject: [PATCH 21/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 42b7e638c..d7313d447 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -317,7 +317,7 @@ def parse_file(self, fh: TextIO) -> None:
while not tree and errors < limit:
try:
- tree = self._tree(ElementTree.fromstring(document))
+ tree = self._tree(ElementTree.fromstring(document), root=True)
break
except ElementTree.ParseError as err:
errors += 1
From 8cdfc19a98236c2c977be5a208bca7d23f18fc71 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:14:37 +0100
Subject: [PATCH 22/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index d7313d447..0369200ca 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -262,6 +262,7 @@ class Xml(ConfigurationParser):
def _tree(self, tree: ElementTree, root: bool = False) -> dict:
"""Very simple but robust xml -> dict implementation, see comments."""
nodes = {}
+ result = {}
counter = {}
# each node is a folder (so the structure is always the same! [1])
From 416dfb7241074d3f1c7538ea10592853b0ca5314 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:14:45 +0100
Subject: [PATCH 23/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 0369200ca..1b594b2a2 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -275,7 +275,6 @@ def _tree(self, tree: ElementTree, root: bool = False) -> dict:
counter[node.tag] = 1
nodes[node.tag] = self._tree(node)
- result = {"tag": tree.tag}
# all attribs go in the attribute folder
# (i.e. stable, does not change depending on xml structure! [2]
From 8066e7a072810a490cb817227a109518d7cde5f9 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:14:54 +0100
Subject: [PATCH 24/25] Update dissect/target/helpers/configutil.py
Co-authored-by: Stefan de Reuver <9864602+Horofic@users.noreply.github.com>
---
dissect/target/helpers/configutil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 1b594b2a2..6cdf0a3f3 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -293,7 +293,7 @@ def _tree(self, tree: ElementTree, root: bool = False) -> dict:
result["text"] = text
# if you need to store meta-data, you can extend add more entries here... CDATA, Comments, errors
-
+ result = {tree.tag: result} if root else result
return result
def _fix(self, content: str, position: tuple(int, int)) -> str:
From 8a9992a87b2ea3cbbf9a098d9446083e937f4865 Mon Sep 17 00:00:00 2001
From: cecinestpasunepipe
<110607403+cecinestpasunepipe@users.noreply.github.com>
Date: Thu, 25 Jan 2024 15:22:57 +0100
Subject: [PATCH 25/25] Implement feedback.
---
dissect/target/helpers/configutil.py | 1 -
tests/filesystems/test_config.py | 94 +++++++++++++---------------
2 files changed, 44 insertions(+), 51 deletions(-)
diff --git a/dissect/target/helpers/configutil.py b/dissect/target/helpers/configutil.py
index 6cdf0a3f3..f0993cd03 100644
--- a/dissect/target/helpers/configutil.py
+++ b/dissect/target/helpers/configutil.py
@@ -275,7 +275,6 @@ def _tree(self, tree: ElementTree, root: bool = False) -> dict:
counter[node.tag] = 1
nodes[node.tag] = self._tree(node)
-
# all attribs go in the attribute folder
# (i.e. stable, does not change depending on xml structure! [2]
# Also, this way we "know" they have been attributes, i.e. we don't lose information! [3]
diff --git a/tests/filesystems/test_config.py b/tests/filesystems/test_config.py
index c9d7dd830..2041ab2dc 100644
--- a/tests/filesystems/test_config.py
+++ b/tests/filesystems/test_config.py
@@ -76,57 +76,51 @@ def mapped_file(test_file: str, fs_unix: VirtualFilesystem) -> VirtualFilesystem
(
"_data/helpers/configutil/test.xml",
{
- "tag": "Server",
- "attributes": {"port": "8005", "shutdown": "SHUTDOWN"},
- "nodes": {
- "Listener": {
- "tag": "Listener",
- "attributes": {"className": "org.apache.catalina.core.JasperListener1"},
- "text": "a",
- },
- "Listener-2": {
- "tag": "Listener",
- "attributes": {"className": "org.apache.catalina.core.JasperListener2"},
- "text": "b",
- },
- "Service": {
- "tag": "Service",
- "attributes": {"name": "Catalina"},
- "nodes": {
- "Connector": {
- "tag": "Connector",
- "attributes": {
- "port": "8080",
- "protocol": "HTTP/1.1",
- "connectionTimeout": "20000",
- "redirectPort": "8443",
+ "Server": {
+ "attributes": {"port": "8005", "shutdown": "SHUTDOWN"},
+ "nodes": {
+ "Listener": {
+ "attributes": {"className": "org.apache.catalina.core.JasperListener1"},
+ "text": "a",
+ },
+ "Listener-2": {
+ "attributes": {"className": "org.apache.catalina.core.JasperListener2"},
+ "text": "b",
+ },
+ "Service": {
+ "attributes": {"name": "Catalina"},
+ "nodes": {
+ "Connector": {
+ "attributes": {
+ "port": "8080",
+ "protocol": "HTTP/1.1",
+ "connectionTimeout": "20000",
+ "redirectPort": "8443",
+ },
},
- },
- "Engine": {
- "tag": "Engine",
- "attributes": {"name": "Catalina", "defaultHost": "localhost"},
- "nodes": {
- "Host": {
- "tag": "Host",
- "attributes": {
- "name": "localhost",
- "appBase": "webapps",
- "unpackWARs": "true",
- "autoDeploy": "true",
- },
- "nodes": {
- "Valve": {
- "tag": "Valve",
- "attributes": {
- "className": "org.apache.catalina.valves.AccessLogValve",
- "directory": "logs",
- "prefix": "localhost_access_log.",
- "suffix": ".txt",
- "pattern": "%h %l %u %t s",
- },
- }
- },
- }
+ "Engine": {
+ "attributes": {"name": "Catalina", "defaultHost": "localhost"},
+ "nodes": {
+ "Host": {
+ "attributes": {
+ "name": "localhost",
+ "appBase": "webapps",
+ "unpackWARs": "true",
+ "autoDeploy": "true",
+ },
+ "nodes": {
+ "Valve": {
+ "attributes": {
+ "className": "org.apache.catalina.valves.AccessLogValve",
+ "directory": "logs",
+ "prefix": "localhost_access_log.",
+ "suffix": ".txt",
+ "pattern": "%h %l %u %t s",
+ },
+ }
+ },
+ }
+ },
},
},
},