From 9faa2d2e2ea92605017663ce2cdbfb8b0de034db Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 27 Nov 2025 15:01:40 +1100 Subject: [PATCH 1/3] Revert "Use defusedxml" This reverts commit 1a7359fc3cf6c3dd7e86030ebcdc64c18efae5a3. --- poetry.lock | 13 +------------ pyproject.toml | 1 - src/jinjaturtle/core.py | 2 +- tests/test_core.py | 2 +- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/poetry.lock b/poetry.lock index b455963..8891448 100644 --- a/poetry.lock +++ b/poetry.lock @@ -461,17 +461,6 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] -[[package]] -name = "defusedxml" -version = "0.7.1" -description = "XML bomb protection for Python stdlib modules" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, -] - [[package]] name = "desktop-entry-lib" version = "5.0" @@ -1193,4 +1182,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "b9153226d96d26f633a7d95ba83b05e78a0063d4c5471b5e0d5f928a4cae0a57" +content-hash = "17e97a5516576384aafd227385b42be9178527537a52ab44e8797816534b5193" diff --git a/pyproject.toml b/pyproject.toml index 01b192b..977325b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ repository = "https://git.mig5.net/mig5/jinjaturtle" python = "^3.10" PyYAML = "^6.0" tomli = { version = "^2.0.0", python = "<3.11" } -defusedxml = "^0.7.1" [tool.poetry.group.dev.dependencies] pytest = "^7.0" diff --git a/src/jinjaturtle/core.py b/src/jinjaturtle/core.py index 5541b61..a4dce7e 100644 --- a/src/jinjaturtle/core.py +++ b/src/jinjaturtle/core.py @@ -2,10 +2,10 @@ from __future__ import annotations import configparser import json +import xml.etree.ElementTree as ET import yaml from collections import Counter, defaultdict -from defusedxml import ElementTree as ET from pathlib import Path from typing import Any, Iterable diff --git a/tests/test_core.py b/tests/test_core.py index ba692cb..8e65697 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,11 +1,11 @@ from __future__ import annotations -from defusedxml import ElementTree as ET from pathlib import Path import configparser import pytest import textwrap import yaml +import xml.etree.ElementTree as ET import jinjaturtle.core as core from jinjaturtle.core import ( From 910234ed652e9b4515a3072f9dc48810a9c9f3c1 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 27 Nov 2025 15:10:45 +1100 Subject: [PATCH 2/3] use defusedxml, silence bandit warnings --- poetry.lock | 13 ++++++++++++- pyproject.toml | 1 + src/jinjaturtle/cli.py | 2 ++ src/jinjaturtle/core.py | 12 +++++++----- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8891448..b455963 100644 --- a/poetry.lock +++ b/poetry.lock @@ -461,6 +461,17 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + [[package]] name = "desktop-entry-lib" version = "5.0" @@ -1182,4 +1193,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "17e97a5516576384aafd227385b42be9178527537a52ab44e8797816534b5193" +content-hash = "b9153226d96d26f633a7d95ba83b05e78a0063d4c5471b5e0d5f928a4cae0a57" diff --git a/pyproject.toml b/pyproject.toml index 977325b..01b192b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ repository = "https://git.mig5.net/mig5/jinjaturtle" python = "^3.10" PyYAML = "^6.0" tomli = { version = "^2.0.0", python = "<3.11" } +defusedxml = "^0.7.1" [tool.poetry.group.dev.dependencies] pytest = "^7.0" diff --git a/src/jinjaturtle/cli.py b/src/jinjaturtle/cli.py index 8158cf4..ce096c4 100644 --- a/src/jinjaturtle/cli.py +++ b/src/jinjaturtle/cli.py @@ -2,6 +2,7 @@ from __future__ import annotations import argparse import sys +from defusedxml import defuse_stdlib from pathlib import Path from .core import ( @@ -47,6 +48,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: def _main(argv: list[str] | None = None) -> int: + defuse_stdlib() parser = _build_arg_parser() args = parser.parse_args(argv) diff --git a/src/jinjaturtle/core.py b/src/jinjaturtle/core.py index a4dce7e..753da81 100644 --- a/src/jinjaturtle/core.py +++ b/src/jinjaturtle/core.py @@ -2,7 +2,7 @@ from __future__ import annotations import configparser import json -import xml.etree.ElementTree as ET +import xml.etree.ElementTree as ET # nosec import yaml from collections import Counter, defaultdict @@ -103,8 +103,9 @@ def parse_config(path: Path, fmt: str | None = None) -> tuple[str, Any]: if fmt == "xml": text = path.read_text(encoding="utf-8") - parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=False)) - root = ET.fromstring(text, parser=parser) + # defusedxml.defuse_stdlib() is called in CLI entrypoint + parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=False)) # nosec + root = ET.fromstring(text, parser=parser) # nosec return fmt, root raise ValueError(f"Unsupported config format: {fmt}") @@ -868,8 +869,9 @@ def _generate_xml_template_from_text(role_prefix: str, text: str) -> str: prolog, body = _split_xml_prolog(text) # Parse with comments included so are preserved - parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True)) - root = ET.fromstring(body, parser=parser) + # defusedxml.defuse_stdlib() is called in CLI entrypoint + parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True)) # nosec + root = ET.fromstring(body, parser=parser) # nosec _apply_jinja_to_xml_tree(role_prefix, root) From 3840b7181220274c09dd1848f0d9cef61b9e66b7 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 27 Nov 2025 15:21:17 +1100 Subject: [PATCH 3/3] Satisfy the needs of defusedxml.defuse_stdlib() whilst still retaining functionality and passing tests --- src/jinjaturtle/core.py | 11 +++++------ tests/test_core.py | 6 ++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/jinjaturtle/core.py b/src/jinjaturtle/core.py index 753da81..5da35af 100644 --- a/src/jinjaturtle/core.py +++ b/src/jinjaturtle/core.py @@ -2,7 +2,7 @@ from __future__ import annotations import configparser import json -import xml.etree.ElementTree as ET # nosec +import xml.etree.ElementTree as ET # nosec import yaml from collections import Counter, defaultdict @@ -103,9 +103,7 @@ def parse_config(path: Path, fmt: str | None = None) -> tuple[str, Any]: if fmt == "xml": text = path.read_text(encoding="utf-8") - # defusedxml.defuse_stdlib() is called in CLI entrypoint - parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=False)) # nosec - root = ET.fromstring(text, parser=parser) # nosec + root = ET.fromstring(text) # nosec B314 return fmt, root raise ValueError(f"Unsupported config format: {fmt}") @@ -870,8 +868,9 @@ def _generate_xml_template_from_text(role_prefix: str, text: str) -> str: # Parse with comments included so are preserved # defusedxml.defuse_stdlib() is called in CLI entrypoint - parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True)) # nosec - root = ET.fromstring(body, parser=parser) # nosec + parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=True)) # nosec B314 + parser.feed(body) + root = parser.close() _apply_jinja_to_xml_tree(role_prefix, root) diff --git a/tests/test_core.py b/tests/test_core.py index 8e65697..53e979c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -566,8 +566,7 @@ def test_generate_template_xml_structural_fallback(): """ ) - parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=False)) - root = ET.fromstring(xml_text, parser=parser) + root = ET.fromstring(xml_text) tmpl = generate_template("xml", parsed=root, role_prefix="role") @@ -643,8 +642,7 @@ def test_flatten_xml_text_with_attributes_uses_value_suffix(): the text at path + ('value',), not just path. """ xml_text = "text" - parser = ET.XMLParser(target=ET.TreeBuilder(insert_comments=False)) - root = ET.fromstring(xml_text, parser=parser) + root = ET.fromstring(xml_text) items = flatten_config("xml", root)