Skip to main content

purexml logo

purexml

Safely parse untrusted XML using only the Python standard library.

tests Python 3.10–3.13 runtime deps: zero pure stdlib coverage ≥90% lint: ruff types: mypy differentially fuzzed License: MIT

purexml is a zero-dependency, drop-in replacement for defusedxml. It hardens Python's standard-library XML parsers against the known attack classes — entity-expansion bombs, external-entity resolution (XXE), and external-DTD retrieval — and hands back the same standard xml.etree / minidom / SAX objects your code already expects. Migrating is a literal find-and-replace: s/defusedxml/purexml/.

How purexml defends untrusted XML in two layers: purexml's own Python-layer handlers block entity bombs, XXE and external-DTD resolution version-independently, while the libexpat parser layer mitigates parser-level DoS depending on your runtime; the result is a standard, trusted xml.etree tree.

Why purexml

  • Zero runtime dependencies. Pure standard library — drop a third-party dependency with no loss of protection (same parses succeed, same attacks blocked).
  • A real drop-in. Mirrors defusedxml's API and defaults across the surface the ecosystem actually imports, validated oracle-gated against defusedxml itself.
  • Maintained. defusedxml has been frozen since 2021 — the XML threat landscape hasn't. purexml tracks the moving libexpat / CPython mitigations and can report what your runtime is actually protected against.
  • Opt-in hardening. Structural-DoS caps and a posture report that defusedxml lacks — off by default, so the drop-in promise is never broken.

Install & migrate

Until purexml is published to PyPI, depend on it via a git or path reference. Then the only change to your code is the import path:

# before:  from defusedxml.ElementTree import fromstring
from purexml.ElementTree import fromstring

root = fromstring(untrusted_xml)   # raises on bomb / XXE / external DTD; returns a standard Element

Defaults match defusedxml exactly (forbid_entities=True, forbid_external=True, forbid_dtd=False), so nothing else changes.

Runnable examples for every surface: examples/ · Module-by-module migration guide: docs/MIGRATING.md

What it covers

Drop-in replacements for the defusedxml modules the ecosystem imports:

purexml module replaces surface
purexml.ElementTree defusedxml.ElementTree fromstring, parse, iterparse, XML, XMLParser, tostring, forbid_*
purexml.minidom defusedxml.minidom parse, parseString
purexml.sax / .expatreader defusedxml.sax make_parser, parse, parseString
purexml.xmlrpc defusedxml.xmlrpc monkey_patch, unmonkey_patch
purexml.common defusedxml.common exception catch-site aliases

defusedxml.pulldom is deferred (low measured demand); defusedxml.lxml is excluded by the zero-dependency contract (it wraps the third-party lxml).

Opt-in defense-in-depth

Bounded protections defusedxml never had — default-off, so the strict mirror is unchanged until you ask:

import purexml
from purexml import fromstring, RECOMMENDED_LIMITS

# Bound structural DoS (deep nesting / attribute floods / giant documents):
root = fromstring(untrusted, limits=RECOMMENDED_LIMITS)   # raises LimitExceeded past the caps

# See what THIS runtime is actually protected against (read-only; no parse):
print(purexml.security_report())

There's also a posture CLI — python -m purexml (--json for machine output, or --check --min-expat X.Y.Z as an opt-in CI gate).

Status

v1.0.0 — the public contract is frozen and binding. The defusedxml-mirror surface won't move under you without a 2.0 (see PUBLIC_CONTRACT.md).

  • Runs on CPython 3.10–3.13, zero runtime dependencies.
  • Correctness is oracle-gated against defusedxml every release — C14N + event-stream equivalence over a real corpus, an adversarial attack battery, and differential fuzzing (see docs/EQUIVALENCE.md).
  • The contract freeze was ratified with the anchor consumer (file-observer) — see docs/v1.0.0_RFC_Specification.md. The opt-in defense-in-depth (Limits, security_report()) stays provisional (it tracks the moving libexpat threat landscape).
  • Not yet published to PyPI, and the purexml name is not yet claimed — distribution is a separate, deliberate step. Depend on it via git/path until then.
  • License: MIT.

Documentation

License

MIT — purexml is give-it-away, zero-dependency infrastructure: pure open source, maximum reuse. Publishing to PyPI and claiming the purexml name remain a separate, deliberate call.

Metadata

Release files for purexml 1.0.0

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

Source distribution (sdist)

Source distribution for purexml 1.0.0
File Size Uploaded Provenance
purexml-1.0.0.tar.gz 69.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for purexml 1.0.0
File Interpreter ABI Platform
purexml-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 105.7 kB

Release files / purexml-1.0.0.tar.gz

Download URL purexml-1.0.0.tar.gz
Size 69.7 kB
Tags Source
SHA-256 checksum
How to use checksums
bfd03bd7107dafe0868436246497b6cf5ea38180db0a65027f3fd04504afa254
BLAKE2b-256 checksum
How to use checksums
1e252fec55ef7bbeb2c92ee362d9b2309ddffd2dfa1887ab9d473aab065e6495
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

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 Jul 8, 2026.

Transparency log

Release files / purexml-1.0.0-py3-none-any.whl

Download URL purexml-1.0.0-py3-none-any.whl
Size 36.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
19f5cf53b1e9e98ffb6ed4ad7c352933c9a370141778d544ea950670c25e19b2
BLAKE2b-256 checksum
How to use checksums
7175506c1ade0ac0f4cecbf4d716f62adf78e592788679d88294bfdb62e9829b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

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 Jul 8, 2026.

Transparency log

Security for purexml 1.0.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for purexml-1.0.0.tar.gz

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 Jul 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files Full provenance

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