diff --git a/graphify/manifest_ingest.py b/graphify/manifest_ingest.py index 717d9ad9a..424e72ff1 100644 --- a/graphify/manifest_ingest.py +++ b/graphify/manifest_ingest.py @@ -269,6 +269,11 @@ def _parse_gomod(text: str) -> dict | None: def _parse_pom(text: str) -> dict | None: + # ElementTree does not cap entity expansion, so reject declarations before + # parsing rather than allowing a crafted POM to consume excessive memory. + lowered = text.lower() + if "\n' ' com.acme\n widget\n 2.0\n' @@ -75,6 +75,15 @@ def test_pom_parses_artifact_and_deps(tmp_path): assert any(e["target"] == "pkg_org_lib_core" for e in r["edges"]) +def test_pom_with_internal_entity_is_rejected_before_parsing(tmp_path): + p = _write(tmp_path / "pom.xml", + ']>\n' + '&artifact;\n') + r = extract_package_manifest(p) + assert r["nodes"] == [] and r["edges"] == [] + assert r["error"] == "manifest parse error: refusing XML with DOCTYPE/ENTITY declaration" + + # ── #1377: a package referenced by N manifests is ONE node ─────────────────── def test_apm_dependency_collapses_to_single_canonical_node(tmp_path):