CVE-2026-54911 Overview
CVE-2026-54911 is an input validation vulnerability [CWE-20] in UltraJSON (ujson), a high-performance JSON encoder and decoder written in C with bindings for Python 3.7+. Versions prior to 5.13.0 mishandle malformed UTF-8 byte sequences when ujson.dumps(), ujson.dump(), or ujson.encode() is called with the reject_bytes=False option. Instead of rejecting truncated or invalid UTF-8 input, the library silently rewrites those bytes into different Unicode characters. This behavior enables input validation bypass and introduces data integrity issues for any application relying on ujson to faithfully serialize byte strings.
Critical Impact
Applications using reject_bytes=False may pass attacker-controlled byte sequences through ujson and emit silently-modified JSON, breaking integrity guarantees and enabling validation bypass downstream.
Affected Products
- UltraJSON (ujson) versions prior to 5.13.0
- Python 3.7+ applications calling ujson.dumps(), ujson.dump(), or ujson.encode() with reject_bytes=False
- Downstream services that trust ujson output for validation, signing, or replay
Discovery Timeline
- 2026-06-22 - CVE-2026-54911 published to NVD
- 2026-06-23 - Last updated in NVD database
Technical Details for CVE-2026-54911
Vulnerability Analysis
UltraJSON exposes a reject_bytes parameter that, when set to False, allows the encoder to accept bytes input and emit a JSON string. The encoder assumes the input is well-formed UTF-8 but does not fully validate continuation bytes or guard against truncated multi-byte sequences. As a result, malformed input is normalized into replacement or substitute Unicode code points rather than rejected. Attackers who can supply bytes to a service that re-serializes them with ujson can produce output that differs from the input while still being treated as valid by validators that ran on the original bytes.
Root Cause
The defect lives in the UTF-8 decoding paths of src/ujson/lib/ultrajsondec.c and src/ujson/lib/ultrajsonenc.c. The 2-byte UTF-8 branch did not verify that a continuation byte was present and that it carried the expected 10xxxxxx bit pattern. The encoder also failed to require at least two bytes of remaining input before reading the continuation byte. These omissions allowed overlong, truncated, and structurally invalid sequences to be silently coerced into arbitrary Unicode scalar values.
Attack Vector
Exploitation is network-reachable and requires no authentication or user interaction. An attacker submits a payload containing crafted byte sequences to an API or service that forwards bytes into ujson.dumps(..., reject_bytes=False). The encoder produces a JSON document containing Unicode characters that differ from the original bytes. Systems that signed, logged, or validated the pre-encoding bytes may now disagree with consumers that parse the encoded output, enabling validation bypass and integrity drift.
// Patch excerpt: src/ujson/lib/ultrajsonenc.c
// Adds continuation-byte validation for 2-byte UTF-8 sequences
case 2:
{
JSUTF32 in;
JSUTF16 in16;
if (end - io < 2)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
return FALSE;
}
if ((io[1] & 0xc0) != 0x80)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Invalid continuation byte in 2-byte UTF-8 sequence detected when encoding string");
return FALSE;
}
memcpy(&in16, io, sizeof(JSUTF16));
in = (JSUTF32) in16;
Source: GitHub commit 169eaf3
The decoder receives a parallel hardening for overlong 2-byte sequences in ultrajsondec.c, ensuring that decoded code points below 0x80 are rejected with an explicit error rather than emitted as valid characters.
Detection Methods for CVE-2026-54911
Indicators of Compromise
- JSON outputs containing the Unicode replacement character (U+FFFD) or unexpected Latin-1 substitutions where binary input was supplied
- Application logs showing successful encoding of payloads that fail downstream UTF-8 validators
- Mismatch between request-body hashes and the hash of the re-serialized JSON stored or forwarded by the service
Detection Strategies
- Inventory Python dependencies for ujson versions below 5.13.0 using pip list or SBOM tooling
- Grep source repositories for calls to ujson.dumps, ujson.dump, or ujson.encode that pass reject_bytes=False
- Add unit tests that submit known-malformed UTF-8 (truncated 2-byte sequences, lone continuation bytes) and assert the encoder raises rather than rewrites
Monitoring Recommendations
- Alert on integrity failures between pre- and post-serialization payload hashes in API gateways
- Monitor for spikes in JSON parse errors at downstream consumers, which can indicate upstream silent rewriting
- Track package upgrade status for ujson across build pipelines and container images
How to Mitigate CVE-2026-54911
Immediate Actions Required
- Upgrade UltraJSON to version 5.13.0 or later across all Python environments
- Audit application code for reject_bytes=False usage and remove it where str input is acceptable
- Validate UTF-8 input at the application boundary before passing bytes to any JSON encoder
Patch Information
The issue is fixed in UltraJSON 5.13.0. The patch adds explicit continuation-byte and length checks in both the encoder and decoder paths. Refer to the GitHub Security Advisory GHSA-3j69-69wj-xqx2 and the 5.13.0 release notes for full details.
Workarounds
- Stop passing reject_bytes=False; allow ujson to reject bytes input by default and convert to str explicitly
- Pre-validate byte payloads with bytes.decode('utf-8', errors='strict') before encoding to JSON
- Switch temporarily to the standard library json module for paths that must accept untrusted bytes
# Upgrade ujson to the patched release
pip install --upgrade 'ujson>=5.13.0'
# Verify installed version
python -c "import ujson; print(ujson.__version__)"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

