Introduction
In the production codebase of a web service, the file utils/commands_extractors/find_java_repo_commands.py was responsible for parsing XML content—likely Maven POM files or similar Java project configuration—to extract build commands. At line 7, it imported Python's native xml.etree.ElementTree library:
import xml.etree.ElementTree as ET
This single import created a high-severity attack surface. Because this code runs in a web service context where request handlers process external input, a remote attacker could craft malicious XML payloads to read sensitive files from the server, perform Server-Side Request Forgery (SSRF), or trigger denial-of-service conditions.
The Vulnerability Explained
Python's standard library XML parsers—including xml.etree.ElementTree, xml.dom.minidom, and xml.sax—do not disable Document Type Definition (DTD) processing by default. This means they will happily resolve external entities defined in a <!DOCTYPE> declaration.
Here's the vulnerable import in find_java_repo_commands.py:
import xml.etree.ElementTree as ET
When find_java_repo_commands() processes XML input using this library, an attacker can submit a payload like:
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><command>&xxe;</command></root>
When the parser encounters &xxe;, it resolves the entity by reading /etc/passwd from the server's filesystem. The contents of that file then appear in the parsed XML output—potentially returned to the attacker or logged where they can access it.
Attack Scenarios Specific to This Code
Since find_java_repo_commands() extracts commands from XML (likely parsing pom.xml or similar files from repositories), an attacker who can influence the XML content being parsed could:
- File Disclosure: Inject
<!ENTITY xxe SYSTEM "file:///etc/shadow">to exfiltrate credential hashes - SSRF: Use
<!ENTITY xxe SYSTEM "http://internal-server.local/admin">to probe internal network services - Denial of Service: Reference
file:///dev/randomor use recursive entity expansion (the "Billion Laughs" attack) to exhaust server memory
The PR description confirms this is a web service where "vulnerabilities in request handlers are directly exploitable by remote attackers," making this a critical exposure point.
The Fix
The fix is elegant in its simplicity—a single-line change that swaps the vulnerable native library for a hardened alternative:
Before (Vulnerable)
import xml.etree.ElementTree as ET
After (Fixed)
import defusedxml.ElementTree as ET
That's it. Because defusedxml provides a drop-in replacement API that mirrors xml.etree.ElementTree, no other code changes are needed. The find_java_repo_commands() function continues to work identically for valid XML inputs, but now:
- DTD processing is disabled —
<!DOCTYPE>declarations are rejected - External entity resolution is blocked —
SYSTEMandPUBLICentities cannot fetch files or URLs - Entity expansion is limited — recursive/nested entity attacks are prevented
The defusedxml library achieves this by wrapping the native parser with secure defaults, explicitly setting forbid_dtd=True, forbid_entities=True, and forbid_external=True at the parser level.
Verification
The PR includes a comprehensive regression test that validates the security invariant with multiple XXE payloads:
@pytest.mark.parametrize("payload", [
# Valid input (boundary case)
"<?xml version='1.0'?><root><command>mvn clean</command></root>",
# XXE exploit payload - attempts to read sensitive file
"""<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><command>&xxe;</command></root>""",
# ... additional payloads
])
def test_xml_processing_resists_xxe_attacks(payload):
# Ensures no sensitive data leaks through
This test confirms that after the fix, malicious payloads either raise safe exceptions or return empty/filtered results—never leaked file contents.
Prevention & Best Practices
1. Never Use Native Python XML Libraries for Untrusted Input
The Python documentation itself warns about this. Always use defusedxml when parsing XML from external sources:
pip install defusedxml
2. Apply the Principle of Least Privilege to Parsers
Even if you think your XML input is trusted, defense in depth means disabling features you don't need. You almost never need DTD processing or external entity resolution.
3. Use SAST Tools to Catch Unsafe Imports
Bandit rules B313-B410 and Semgrep's gitlab.bandit.B313...B410 rule specifically flag imports of native Python XML libraries. Integrate these into your CI/CD pipeline.
4. Audit All XML Parsing Across Your Codebase
If one file uses the native library, others likely do too. Run a grep for import xml. across your project and replace all instances.
5. Consider JSON or YAML Alternatives
If you control the data format, consider whether XML is necessary. JSON and YAML don't have entity expansion features, eliminating this attack class entirely.
Key Takeaways
xml.etree.ElementTreeis unsafe for untrusted input — Python's native XML libraries are explicitly documented as vulnerable to XXE attacksdefusedxmlis a drop-in replacement — changing one import line infind_java_repo_commands.pyfixed the vulnerability without modifying any parsing logic- Web services parsing repository XML are high-value targets — since
find_java_repo_commands()processes XML from repositories (likepom.xml), any attacker who can influence repository content can exploit XXE - Static analysis catches this pattern reliably — Bandit/Semgrep rules specifically target native XML library imports, making this a fully automatable detection
- One vulnerable import can compromise an entire server — from a single
import xml.etree.ElementTree, an attacker can read arbitrary files, scan internal networks, and cause denial of service
How Orbis AppSec Detected This
- Source: XML content from external repositories processed by the
find_java_repo_commands()function - Sink:
xml.etree.ElementTreeparser invocations inutils/commands_extractors/find_java_repo_commands.py:7 - Missing control: No restriction on DTD processing or external entity resolution in the XML parser
- CWE: CWE-611 (Improper Restriction of XML External Entity Reference)
- Fix: Replaced
import xml.etree.ElementTree as ETwithimport defusedxml.ElementTree as ETto disable external entity processing by default
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
XXE vulnerabilities in Python are uniquely dangerous because they hide behind an innocent-looking standard library import. The native xml.etree.ElementTree module provides no protection against malicious DTD declarations, and Python's own documentation recommends against using it for untrusted input. In a web service context like this one—where find_java_repo_commands.py parses XML from external repositories—the exposure is direct and exploitable.
The fix demonstrates that security improvements don't have to be complex. A single import change from xml.etree.ElementTree to defusedxml.ElementTree closes the vulnerability completely while maintaining full API compatibility. Make this substitution a standard practice in every Python project that handles XML.