Date: 2026-06-16 Target: aiohttp 3.14.1 (patched 3.13.4 bundle: CVE-2026-22815/34516/34519/34520/34525) Verdict: WALK CLEAN.
aiohttp's classic incomplete-fix shape = the Cython (_http_writer/_http_parser, DEFAULT) and pure-Python
fallback paths DIVERGING after a fix. Tested both directly:
- Writer header-injection/response-splitting (34519/34520): differential-tested CR/LF/NUL/DEL/VT/FF/US in
status line + header name + header value across _py_serialize_headers vs _c_serialize_headers. IDENTICAL
(both reject all, both allow TAB). Regex [\x00-\x08\x0a-\x1f\x7f] applied to status+name+value in both.
- Parser dual-Host (34525): fed raw Host: a\r\nHost: b through C parser (default) and Python parser
(AIOHTTP_NO_EXTENSIONS=1). BOTH reject (BadHttpMessage), both accept single Host. "host" is in
SINGLETON_HEADERS; server request parser is strict (_lax=False) so the dedup fires by default.
- Caps present: max_line_size=8190, max_field_size=8190, max_headers=32768(req), max_trailers=128 (22815/34516).
aiohttp = disciplined SWEEPER (C/Python parity maintained). No gap.
aiohttp._http_writer vs aiohttp.http_writer._py_*); call each on the SAME
adversarial inputs; assert identical accept/reject. Divergence = finding.AIOHTTP_NO_EXTENSIONS=1 (env) to force the pure-Python path, run the SAME raw-bytes
battery in each mode. Same technique applies to any lib shipping a C/Rust fast-path + Python fallback
(e.g., pydantic-core, orjson, msgpack, ujson, simdjson bindings, yarl): the fast path is the default and the
one that can silently lag the security fix applied to the fallback.POINT-FIXERS (yielded findings): PyJWT 2.13.0 (242,244), form-data (241), Django-Truncator (248). SWEEPERS (walked): Symfony, undici, axios, Django-alias-SQLi, Django-ASGI, aiohttp. Tell holds: read each fix's SCOPE; for dual-impl libs, parity between fast-path and fallback is the specific thing a sweeper maintains and a point-fixer breaks.