Repository navigation
Detect transformed fixture canaries in Hermes tool results - #217
Conversation
…d on oversize results (#213) Rejoin wrapped base64 before decoding, inflate gzip payloads under a size cap, check reversed, percent-encoded, unicode-escaped, and 0x-prefixed forms, and treat any tool result too large to decode as a self-read instead of skipping it. Exercise the base64url alphabet in tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…#213) Replace the backtracking rejoin regex and sub-run combinations with one linear pass over base64-only lines, trying each group with and without a stray first or last line. Join a tool result's content fields before scanning so split parts are checked together, and cap inspected size. Add linear-time and split, armored, and headed wrapped-base64 tests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replace the per-token cap that flagged large ordinary lines as self-reads with one decode budget per tool result, inflate every concatenated gzip member within it, and fail closed only when the budget is exhausted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review record
All fixes after the original commit are by Claude. Each has a mutation control that turned the targeted test red and then green again after restoring the code. Validation at Merge status: not merged. Three repair cycles are complete. Per the review policy, the remaining findings are filed as #219 rather than fixed in a fourth cycle without approval. |
Plain base64 decoding is bounded by the input cap, so it no longer draws on the decode budget; wrapped lines were charged twice and could flag ordinary output as a self-read. Cap gzip members per tool result so thousands of empty members cannot stall the harness, failing closed above the cap. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Decode wrapped base64 groups before individual lines, charge each group's gzip expansion once by its largest variant, and skip lines already covered by a group. Independent tokens still draw on the budget one by one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s) (#219) A misaligned join of unpadded lines can hide a line, so every group line is also decoded on its own. Both views cover the same bytes, so a group is charged max(largest variant sum, line sum); tokens within one source are summed, which also caps many gzip tokens packed into one group. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replace per-group bookkeeping, which undercounted disjoint variants, with one running total over every inflation. A payload appears in at most four decoded views, so the total cap is four times a 10 MB content allowance: legitimate content is never over-charged, all inflated content is scanned, and total work stays bounded; exceeding the cap fails closed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ce (#219) Parse the gzip header and inflate the raw deflate stream in chunks, so a corrupt CRC or truncated member no longer discards decoded text and member steps do not copy the whole remaining payload. Scale the member allowance by the same overlapping-view factor so valid multi-member input seen through several views is not flagged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…219) A deflate error, an unreadable header with data remaining, or gzip data left after the members now fails closed; running out of data mid-member stays complete. Skip the 8-byte trailer only when the next member lines up, so a missing trailer cannot hide the following member. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A stored deflate block can carry a following member verbatim, hiding its canary inside the first member's output. Inflate every gzip signature in inflated output as a nested member, sharing the budget and member allowance; nested errors are ignored because the attempts are speculative. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Check reversed markers against each view instead of storing reversed copies, and strip non-hex characters with a byte-level delete instead of a regex. A 48 KB result decompressing to 36 MB drops from about 12 s to 0.14 s. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ature (#219) Strip the reversed x0 prefix form before the reversed-marker check, and inflate exactly one member at each nested gzip signature so concatenated inner members are not re-inflated for every suffix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review record, fourth cycle (approved by the maintainer)The findings filed as #219 were fixed, and each subsequent re-check was fixed in turn:
Every fix has a mutation control: the targeted test goes red with the fix removed or reverted, and green when restored. Validation at Remaining documented limit (README): discovery evidence stays bounded against arbitrary transformations. Output containing hundreds of gzip signature bytes fails closed, because it exhausts the member allowance. |
Fixes #213. Fixes #219.
The live harness now checks tool-result text for literal fixture canaries, case changes, separators in 32-character hex canaries, and base64/base64url encoding before redacting the result. This restores self-read evidence when a tool transforms the canary. The README describes the bounded detection.
Validation: focused Hermes harness regression and existing self-read tests passed (5 tests);
compileallandgit diff --checkpassed. The full harness suite was attempted but did not finish promptly in this environment, so it is not claimed as passing.Later cycles (see the review records) added wrapped/gzip/nested detection, a bounded decode budget, member allowances, and linear-time matching.