v0.1.2 — critical security release
Critical security release. Upgrade from 0.1.1 and 0.1.0.
A follow-up adversarial audit confirmed the 0.1.1 fixes hold, but found a critical enforcement bypass shared by both earlier versions.
The issue
The proxy policed the parsed message but forwarded the raw bytes verbatim. Because a child can split that byte range differently than the proxy parsed it, a tools/call could execute without ever being policed or journaled — and aileron verify still reported OK, because nothing was tampered with; the call was simply never recorded.
Two working variants, both reproduced end-to-end against the published wheel:
- Header smuggling — every line before the blank line was accumulated as "headers" and forwarded. A JSON-RPC message parked on its own line there is invisible to policy but executed by a newline-delimited child. 148 bytes was enough.
- Body re-splitting — a
Content-Lengthbody may legally contain raw newlines, so the proxy saw one frame where the child saw several.
The damaging shape: with a block rule demonstrably active, a smuggled shell call exfiltrating ~/.ssh/id_rsa executed on the child while the journal recorded a different shell call as blocked. An auditor reading that journal would conclude enforcement worked.
The fix
Structural, not a patch on the specific attacks: the proxy now forwards a re-serialization of the message it policed — compact separators, ASCII-escaped, no raw newlines — in both directions. The message boundary the child sees is the same object the policy engine inspected, by construction. Header lines are validated against RFC 7230 and the header block is bounded.
Also in this release
- Checkpoints are chained (signed
index+prev_checkpoint_hash), so deletion, duplication, or reordering within the sequence is detected. Deleting the newest checkpoint remains tail truncation — now stated explicitly in SECURITY.md. verify()reports raw invalid UTF-8 as tampering instead of raising.- The report badge escapes
countandfirst_bad_seq. - A non-mapping
paramsno longer crashes the proxy;MAX_MESSAGE_BYTESbounds every read path;pendingis capped; the child wait is bounded so the shutdown drain always runs. - A blocked JSON-RPC batch is now answered with a batch response and blames the call that actually matched.
Full detail in CHANGELOG.md. 117 tests.