Development record
Four untrusted-operand stdin readers (readIdeatePayload, readLessonsPayload, readSynthesisPayload, readSource) read exactly cap bytes via a bare io.LimitReader(cap), so an over-cap '-' payload is silently truncated into a severed prefix instead of refused, while each reader's file transport (readGuardedOperand) refuses the same bytes with fsutil.ErrTooBig. Worst on the history capture path: history.Capture performs no parse and no cap check, so an over-cap stdin transcript is stored as an 8 MiB prefix, reported 'stored', under a sha256 idempotency key computed over the prefix — breaking spc-4's refuse-whole invariant. Also lets an over-cap ideate verdict past both the MaxPayloadBytes cap gate and the dec.More() trailing-data gate at a crafted boundary. Unswept siblings of resolved iss-201/iss-347; every site's doc comment claims stdin parity with the file cap.
Four untrusted-operand stdin readers (readIdeatePayload, readLessonsPayload, readSynthesisPayload, readSource) read exactly cap bytes via a bare io.LimitReader(cap), so an over-cap '-' payload is silently truncated into a severed prefix instead of refused, while each reader's file transport (readGuardedOperand) refuses the same bytes with fsutil.ErrTooBig. Worst on the history capture path: history.Capture performs no parse and no cap check, so an over-cap stdin transcript is stored as an 8 MiB prefix, reported 'stored', under a sha256 idempotency key computed over the prefix — breaking spc-4's refuse-whole invariant. Also lets an over-cap ideate verdict past both the MaxPayloadBytes cap gate and the dec.More() trailing-data gate at a crafted boundary. Unswept siblings of resolved iss-201/iss-347; every site's doc comment claims stdin parity with the file cap.
Frontmatter
| id | iss-2608211134281912 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-2608211134281912-stdin-operand-readers-truncate-instead.md
open on GitHub ↗ · commit history ↗
mentions4
- dcn-26082100000000012026-08-21 — Bug-hunt round 4, branch bughunt-b/round-4. Baseline (make preflight
- iss-201abcd guard hook reads its PreToolUse stdin payload via a bare LimitReader with no overflow check, so a payload padded past the 1 MiB cap (e.g. a blocked command with a trailing shell comment full of junk) is silently truncated, breaks JSON parsing, and routes to the fail-open-loud path (exit 1, command runs) with a diagnostic that misleadingly blames the host ('not readable JSON') instead of reporting the actual cap overflow; the sibling check verb's guardCandidate already reads cap+1 specifically to detect and refuse this. Independently verified: threshold is sharp and deterministic (cap-1 blocks, cap+1 fails open), always a loud (not silent) fail-open, exploitation cost bounded by needing >1 MiB of attacker-influenced content inside one tool_input
- iss-347readTranscript opens the hook-supplied transcript path without O_NOFOLLOW or an over-cap probe, the one guarded read left off fsutil.ReadGuarded, so a symlinked transcript_path is followed and an over-cap file is stored silently truncated
- spc-4start-the-transcript-clock