Skip to main content

SafeXML 🛡️

The memory-safe, GIL-free, ultra-fast XML parser and drop-in defusedxml successor built in Rust.

CI PyPI version Python versions Coverage: 100% Rust: 100% Safe GIL: Detached License: MIT


⚡ The Case for SafeXML: Why DefusedXML is Obsolete

For over a decade, Python developers relied on defusedxml to protect against XML bombs. But defusedxml is fundamentally a legacy wrapper around CPython's 1990s-era C pyexpat engine. In modern Python (>=3.12), defusedxml has become a security liability and performance bottleneck:

1. Immune to C Memory Corruption (CVE Hell)

defusedxml is just a Python-level monkeypatch on top of C libexpat. When libexpat suffers from integer overflows, heap buffer overflows, or use-after-free bugs, defusedxml cannot protect you. Between 2022 and 2026 alone, libexpat was hit by a barrage of critical CVEs:

  • CVE-2024-45490, CVE-2024-45491, CVE-2024-45492: Integer overflows in XML parsing causing heap corruption.
  • CVE-2023-52425: Denial of service through entity expansion parser state corruption.
  • CVE-2022-25235, CVE-2022-25236, CVE-2022-23852: Malformed namespace and character encoding heap buffer crashes.

SafeXML is written in 100% memory-safe Rust using quick-xml and PyO3. There is zero C code, zero raw memory pointers, zero heap corruption, and zero use-after-free risk.

2. True GIL Release for Modern Multi-Threaded Services

When parsing XML inside high-concurrency web frameworks (FastAPI, Django, Flask, Celery, gRPC), defusedxml holds Python's Global Interpreter Lock (GIL). Ten worker threads parsing XML become serialized into a single-core crawl.

SafeXML detaches the Python GIL during parsing (py.detach(|| ...)). Rust processes, validates, and decodes the XML stream completely in parallel across all CPU cores, unlocking linear multi-core speedup.

3. Defeats Modern XML Attacks That defusedxml Misses

defusedxml only guards against traditional DTD and entity expansions. It completely misses modern XML attack vectors:

  • Attribute Flood / Attribute Hash DoS (CWE-400): Attackers submit elements with 100,000 attributes. defusedxml constructs a massive Python dictionary, causing quadratic hash collisions and OOM. SafeXML enforces max_attributes (default 1,000).
  • Giant Attribute Value Bombs (CWE-400): A single 50MB attribute value crashes memory. defusedxml does not inspect attribute lengths. SafeXML enforces max_attribute_size (default 10MB).
  • Comment Amplification Bombs: Millions of comments or giant comment streams exhaust parser memory. SafeXML enforces max_comment_size (default 10MB).
  • Tag Name Memory Bombs: Gigantic element tag names consume unbounded memory during string interning. SafeXML enforces max_name_size (default 1,024 chars).
  • Null Byte Injection: Embedded null bytes (\0) in tag or attribute names cause C-string truncation attacks downstream in databases and auth services. SafeXML strictly rejects null bytes.

🥊 Comprehensive Attack Surface & Feature Matrix

Security / Feature Matrix safexml (Rust) defusedxml (Python + C) lxml (C libxml2) xml.etree (Python stdlib)
Billion Laughs / Exponential Entity Bomb 🛡️ BLOCKED 🛡️ BLOCKED ⚠️ Config-dependent ❌ VULNERABLE
Quadratic Blowup Entity Attack 🛡️ BLOCKED 🛡️ BLOCKED ⚠️ Config-dependent ❌ VULNERABLE
External Entity (XXE) / SSRF 🛡️ BLOCKED 🛡️ BLOCKED ⚠️ Config-dependent ❌ VULNERABLE
External DTD Retrieval 🛡️ BLOCKED 🛡️ BLOCKED ⚠️ Config-dependent ❌ VULNERABLE
Attribute Flood / Hash DoS (CWE-400) 🛡️ BLOCKED (max_attributes) ❌ VULNERABLE ❌ VULNERABLE ❌ VULNERABLE
Giant Attribute Value Bomb (CWE-400) 🛡️ BLOCKED (max_attribute_size) ❌ VULNERABLE ❌ VULNERABLE ❌ VULNERABLE
Tag Name Memory Bomb 🛡️ BLOCKED (max_name_size) ❌ VULNERABLE ❌ VULNERABLE ❌ VULNERABLE
Comment Amplification Bomb 🛡️ BLOCKED (max_comment_size) ❌ VULNERABLE ❌ VULNERABLE ❌ VULNERABLE
Null Byte Identifier Injection 🛡️ BLOCKED ❌ Truncated / Allowed ❌ Truncated / Allowed ❌ Truncated / Allowed
Immune to C-Level Memory Corruption 🛡️ YES (Safe Rust) ❌ No (libexpat CVEs) ❌ No (libxml2 CVEs) ❌ No (libexpat CVEs)
Releases Python GIL (Parallel Scaling) ⚡ YES (py.detach) ❌ No (Blocks GIL) ⚠️ Partial ❌ No (Blocks GIL)
Test Coverage Enforced 🎯 100.00% Coverage Unknown / Partial Unknown / Partial N/A
PEP 561 Type Annotations (py.typed) ✅ YES ❌ No (#104) ⚠️ Separate stub ⚠️ Standard library
Rich / IPython Traceback Compatible ✅ YES ❌ Crashes (#105) ✅ Yes ✅ Yes
ElementTree indent() Built-in ✅ YES ❌ Missing (#87) ✅ Yes ✅ Yes
Modern Python Target 🐍 Python >= 3.12 🏚️ Python 2 / Legacy 🐍 All 🐍 Standard library

🚀 Benchmarks: SafeXML vs DefusedXML

Benchmarks run on Linux x86_64, Python 3.12.14, comparing safexml against defusedxml and stdlib xml.etree:

1. Single-Threaded Throughput & Latency

Small Workload (~1 KB XML document):
  SafeXML (Rust):    67.2 µs | 14,879 ops/sec  [1.63x FASTER (+62.9% throughput)] ⚡
  defusedxml:       109.5 µs |  9,133 ops/sec  [Baseline]

Large Workload (~500 KB, 5,000 items):
  SafeXML (Rust):    42.3 ms |    23.6 ops/sec  [1.23x FASTER (+23.3% throughput)] ⚡
  defusedxml:        52.2 ms |    19.2 ops/sec  [Baseline]

2. Multi-Threaded Concurrency (GIL-Release Benchmark)

Under concurrent.futures.ThreadPoolExecutor simulating concurrent API requests:

Worker Threads defusedxml Throughput SafeXML Throughput Real-World Concurrency Speedup
2 Workers 547.6 docs/sec 779.9 docs/sec 1.42x FASTER 🚀
4 Workers 579.8 docs/sec 828.8 docs/sec 1.43x FASTER 🚀
8 Workers 624.1 docs/sec 838.6 docs/sec 1.34x FASTER 🚀

Under heavy multi-threaded workloads, defusedxml saturates the GIL and stalls. safexml frees Python threads to process requests in parallel.


🛠️ Upstream defusedxml Issues Resolved

safexml directly resolves the top open issues and long-standing bugs reported against tiran/defusedxml:

  • tiran/defusedxml#105: Exception formatting crashed when printed by rich tracebacks due to missing args. SafeXML properly populates super().__init__(msg).
  • tiran/defusedxml#104: Complete lack of PEP 561 typing. SafeXML ships with py.typed and comprehensive type annotations.
  • tiran/defusedxml#87: Missing xml.etree.ElementTree.indent(). Full support provided.
  • tiran/defusedxml#80: Missing Element class export. Re-exported directly.
  • tiran/defusedxml#79: Missing register_namespace() helper. Fully supported.
  • tiran/defusedxml#78: fromstring() rejected parser= keyword argument. Standard signature parity supported.
  • tiran/defusedxml#76, #77, #88: defuse_stdlib() monkeypatching broke openpyxl and xmlschema. Safe, non-destructive stdlib defuser implemented.

📦 Installation

Prebuilt abi3 binary wheels are available on PyPI for Linux, macOS (Apple Silicon & Intel), and Windows:

pip install safexml

Requirements: Python >= 3.12.


💡 Quickstart: 10-Second Migration

1. Direct Drop-in Replacement for defusedxml.ElementTree

Simply update your import:

# Before:
# import defusedxml.ElementTree as ET

# After:
import safexml.ElementTree as ET

# 100% identical API, backed by Rust:
root = ET.fromstring("<catalog><item id='1'>Safe XML</item></catalog>")
print(root.tag)               # "catalog"
print(root[0].text)           # "Safe XML"

# Pretty-printing (resolves tiran/defusedxml#87)
ET.indent(root)
print(ET.tostring(root, encoding="unicode"))

2. Global Non-Destructive Stdlib Defusing

If you have third-party dependencies (like openpyxl, boto3, or saml2) using Python's standard xml.etree.ElementTree, you can secure your entire application with one call:

import safexml

# Safely patches xml.etree.ElementTree without breaking third-party packages
safexml.defuse_stdlib()

3. Catching Security Violations

import safexml.ElementTree as ET
from safexml.common import (
    DefusedXmlException,
    DTDForbidden,
    EntitiesForbidden,
    ExternalReferenceForbidden,
)

# 1. Billion Laughs / Exponential Entity Bomb
try:
    ET.fromstring("""<!DOCTYPE bomb [
        <!ENTITY a "1234567890">
        <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;">
    ]><bomb>&a;</bomb>""")
except EntitiesForbidden as exc:
    print(f"Blocked entity expansion: {exc.name}")

# 2. External DTD / XXE / SSRF
try:
    ET.fromstring("""<!DOCTYPE root SYSTEM "http://attacker.com/evil.dtd"><root/>""")
except ExternalReferenceForbidden as exc:
    print(f"Blocked external reference: {exc.sysid}")

# 3. Attribute Flood DoS (CWE-400)
try:
    # Restrict attributes to 5 per element (default: 1,000)
    ET.fromstring("<root a1='1' a2='2' a3='3' a4='4' a5='5' a6='6'/>", max_attributes=5)
except DefusedXmlException as exc:
    print(f"Blocked attribute flood: {exc}")

# 4. Giant Tag Name Bomb
try:
    ET.fromstring(f"<{'A' * 2000}/>", max_name_size=1024)
except DefusedXmlException as exc:
    print(f"Blocked oversized tag: {exc}")

🎯 100% Enforced Code Coverage

SafeXML maintains a strict 100.00% statement and branch coverage requirement across every module. This is enforced directly in CI (pytest --cov-fail-under=100) and git pre-push hooks:

Name                            Stmts   Miss Branch BrPart  Cover
-----------------------------------------------------------------
python/safexml/ElementTree.py      43      0     10      0   100%
python/safexml/__init__.py         13      0      0      0   100%
python/safexml/common.py           41      0      0      0   100%
-----------------------------------------------------------------
TOTAL                              97      0     10      0   100%

🏗️ Architectural Distinction: safexml vs polyxml

Both projects are maintained by the PolyXML organization:

Feature safexml polyxml
Primary Role Untyped DOM / ElementTree drop-in replacement Data-Binding & Serde Engine (Schema-driven)
Output Type xml.etree.ElementTree.Element Typed Python objects (Dataclasses, Pydantic, Attrs)
Primary Use Legacy migrations, SAML, SVG, Office files, arbitrary XML High-throughput APIs, SOAP, microservices, typed pipelines
Security Focus Comprehensive attack surface defense & resource bounding Strict schema validation & zero-copy Rust deserialization

📄 License

MIT License. Engineered with pride under the PolyXML organization by Bailey Nguyen.

Metadata

Release files for safexml 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for safexml 1.0.2
File Size Uploaded
safexml-1.0.2.tar.gz 44.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for safexml 1.0.2
File
safexml-1.0.2-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
safexml-1.0.2-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.12 abi3 Linux glibc 2.17+ x86-64 Details
safexml-1.0.2-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.12 abi3 Linux glibc 2.17+ ARM64 Details
safexml-1.0.2-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details
safexml-1.0.2-cp312-abi3-macosx_10_12_x86_64.whl CPython 3.12 abi3 macOS 10.12+ x86-64 Details

Total release size: 1.2 MB

Release files / safexml-1.0.2.tar.gz

Download URL safexml-1.0.2.tar.gz
Size 44.5 kB
Tags Source
SHA-256 checksum
How to use checksums
087670d519d4a52f7842f23876e4edd6e68229ff4f94d4983d6489e52b5382c8
BLAKE2b-256 checksum
How to use checksums
cb2711712495f578c678cfcf5bd6aa672c1d5fdb35c00077e089bfff5aec08ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / safexml-1.0.2-cp312-abi3-win_amd64.whl

Download URL safexml-1.0.2-cp312-abi3-win_amd64.whl
Size 160.3 kB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
4141796c7aae68c295f0f6ae5089fbeb67526513fa1e888fe273b691f6a4883a
BLAKE2b-256 checksum
How to use checksums
9baf5ba503ef25d123ff37aabf2f25c82203534f9c9a0652aa12787a862bcb64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / safexml-1.0.2-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL safexml-1.0.2-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 242.8 kB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
997f621b860c6f008816a1cafeb5b08cdcb19febbcf6ed577631e98ed8cbf279
BLAKE2b-256 checksum
How to use checksums
8567aab2445044a8f320d9d4618a3bce5dd435fc8ce15716754fba9a603760fc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / safexml-1.0.2-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL safexml-1.0.2-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 236.9 kB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
c7faa449fa29878497c0210ac4e8506074f665a5325d87149c017dfb5155bf6e
BLAKE2b-256 checksum
How to use checksums
18332fa60fedcec8ecc5d09c3591e9b7a773c5d9d54660741cf2556107f355f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / safexml-1.0.2-cp312-abi3-macosx_11_0_arm64.whl

Download URL safexml-1.0.2-cp312-abi3-macosx_11_0_arm64.whl
Size 229.8 kB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
3e94bd43e740b27b8aa0f4cdb60985dfb301e605c6ecd2668302d884538bea16
BLAKE2b-256 checksum
How to use checksums
d7ac7d57c5d53860bce01a47206933c5a4d7492fa65aa2ea4f064951065456be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / safexml-1.0.2-cp312-abi3-macosx_10_12_x86_64.whl

Download URL safexml-1.0.2-cp312-abi3-macosx_10_12_x86_64.whl
Size 236.5 kB
Tags CPython 3.12 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
da315f8b0ce1808228061aa117788730cf5c63e75a636f4e1a70159ed55aad44
BLAKE2b-256 checksum
How to use checksums
7abef9c7d644e798ecb4c561c43992c2bf73d7fbf9ce0e9158e4cdf410219f4b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.2 This release

6 release files

1.0.1

6 release files

1.0.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page