Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@

6.5.1 / 2026-08-05
==================

* security: pin URL connections to validated DNS addresses to prevent DNS-rebinding bypasses of private-network blocking
* security: bound encoded and decoded compressed responses to prevent gzip and deflate resource-exhaustion attacks
* fix: reject XML 1.0-forbidden characters consistently across Python and Rust serializers instead of emitting invalid XML
* fix: require an actual boolean for ``allow_private_networks`` and normalize malformed-hostname failures as ``URLReadError``
* chore: release ``json2xml-rs`` 0.4.3 first and require it from ``json2xml[fast]`` for XML-safe accelerated installs
* docs: document URL safety controls, XML validation behavior, migration guidance, and the Rust-first release sequence


6.5.0 / 2026-07-15
==================

Expand Down
54 changes: 35 additions & 19 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,61 @@
# json2xml_rs 0.4.3
# json2xml 6.5.1

Released 2026-08-05.

## Highlights

- Rejects XML 1.0-forbidden characters in serialized text, attributes, CDATA, and the exported `escape_xml_py()` and `wrap_cdata_py()` helpers instead of emitting invalid XML.
- Gives the Python package a compatible accelerator that passes its XML-safety probe, restoring automatic Rust dispatch for supported payloads.
- Updates PyO3 from 0.28.2 to 0.29.1 while preserving the existing Python 3.9+ package contract and wheel matrix.
- Pins public URL connections to the validated DNS address while preserving the requested Host header and TLS certificate hostname, preventing DNS-rebinding bypasses of private-network blocking.
- Bounds encoded input and incremental gzip or deflate output so compressed responses cannot consume unbounded network I/O or memory before rejection.
- Rejects XML 1.0-forbidden characters consistently across the Python serializer and the newly published Rust accelerator instead of emitting invalid XML.
- Updates `json2xml[fast]` to require the compatible `json2xml-rs>=0.4.3` release.

## Security

- URL reads reject redirects and private, loopback, link-local, and other non-global destinations by default, including destinations reached through DNS rebinding.
- Encoded and decoded response bodies are independently limited to 10 MiB by default.
- Only `gzip`, `x-gzip`, `deflate`, and `identity` content encodings are accepted; malformed or stacked encodings are rejected.
- `allow_private_networks` accepts only `True` or `False`, avoiding truthiness-based policy bypasses.
- Malformed Unicode hostnames consistently raise `URLReadError` when IDNA encoding or DNS resolution fails.

## Migration guidance

Inputs containing forbidden XML 1.0 characters now raise `ValueError`. Remove or replace those characters before conversion; callers must not depend on invalid XML being emitted.
- Trusted callers that intentionally read private endpoints must pass `allow_private_networks=True` as an actual boolean.
- Set `max_response_bytes` explicitly when a trusted endpoint needs a positive response limit other than the 10 MiB default.
- Servers returning Brotli, Zstandard, stacked encodings, or malformed compressed streams must be reconfigured or read outside `readfromurl()`.
- Inputs containing forbidden XML 1.0 characters now fail validation. Low-level serializer functions raise `ValueError`; `Json2xml.to_xml()` raises `InvalidDataError`.

## Package Version
## Package Versions

- Python package: `json2xml==6.5.1`
- Rust accelerator: `json2xml-rs==0.4.3`
- Fast install: `pip install "json2xml[fast]==6.5.1"`

## Verification

The release is gated on Rust formatting, Clippy with warnings denied, Rust unit tests, the full Python suite, and built-wheel tests for Linux, macOS, and Windows before PyPI publication.
The release passed 496 tests with 100% statement coverage, Ruff, ty, sdist and wheel builds, Twine metadata checks, and an isolated install against the published `json2xml-rs==0.4.3` wheel. The pull request also runs the complete cross-platform Python matrix before merge.


# Unreleased
# json2xml_rs 0.4.3

## Security
Released 2026-08-05.

## Highlights

- Public URL reads now pin connections to a validated DNS address while preserving the requested Host header and HTTPS certificate hostname, preventing DNS-rebinding bypasses of private-network blocking.
- Gzip and deflate responses honor valid `Content-Length` values and are decoded incrementally with bounded encoded input and zlib output, so compressed bodies cannot consume unbounded network I/O or memory before rejection.
- `allow_private_networks` accepts only `True` or `False`; strings, numbers, and other values now raise `URLReadError` instead of relying on truthiness.
- Malformed Unicode hostnames now consistently raise `URLReadError` when IDNA encoding or DNS resolution fails.
- Rejects XML 1.0-forbidden characters in serialized text, attributes, CDATA, and the exported `escape_xml_py()` and `wrap_cdata_py()` helpers instead of emitting invalid XML.
- Gives the Python package a compatible accelerator that passes its XML-safety probe, restoring automatic Rust dispatch for supported payloads.
- Updates PyO3 from 0.28.2 to 0.29.1 while preserving the existing Python 3.9+ package contract and wheel matrix.

## Migration guidance

- URL reads continue to reject redirects and private, loopback, link-local, and other non-global destinations by default. Trusted callers that intentionally read a private endpoint must pass `allow_private_networks=True` as an actual boolean.
- Encoded and decoded URL responses are limited to 10 MiB by default. Set `max_response_bytes` explicitly when a trusted endpoint needs a different positive limit.
- URL response compression is limited to `gzip`, `x-gzip`, `deflate`, and `identity`. Servers returning Brotli, Zstandard, stacked encodings, or malformed compressed streams must be reconfigured or read outside `readfromurl()`.
- XML 1.0-forbidden characters are now rejected instead of being serialized. Low-level serializer functions raise `ValueError`; `Json2xml.to_xml()` raises `InvalidDataError`.
Inputs containing forbidden XML 1.0 characters now raise `ValueError`. Remove or replace those characters before conversion; callers must not depend on invalid XML being emitted.

## Package Version

## Release prerequisite
- Rust accelerator: `json2xml-rs==0.4.3`

## Verification

Publish `json2xml-rs==0.4.3`, then raise the `json2xml[fast]` and `uv.lock` minimum to that published version. Until then, the compatibility probe safely disables 0.4.2 and uses the Python backend.
The release passed Rust formatting, Clippy with warnings denied, 48 Rust unit tests, and built-wheel tests across Linux, macOS, Windows, CPython 3.9-3.15, free-threaded builds, and PyPy before PyPI publication.


# json2xml 6.5.0
Expand Down
2 changes: 1 addition & 1 deletion json2xml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

__author__ = """Vinit Kumar"""
__email__ = "mail@vinitkumar.me"
__version__ = "6.5.0"
__version__ = "6.5.1"
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "json2xml"
version = "6.5.0"
version = "6.5.1"
description = "Simple Python Library to convert JSON to XML"
readme = "README.rst"
requires-python = ">=3.10"
Expand Down Expand Up @@ -50,7 +50,7 @@ dev = [
"pygments>=2.20.0",
"xmltodict>=0.12.0",
]
fast = ["json2xml-rs>=0.4.2"]
fast = ["json2xml-rs>=0.4.3"]

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand Down
Loading
Loading