fix(sed): bound output growth during execution - #2455
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 06de33a | Commit Preview URL | Sep 25 2026, 11:35 AM |
The destination cap derived from max_stdout_bytes applied the stdout *capture* limit to data that never reaches captured stdout. sed output piped to another command or redirected to a file was refused once it passed 1 MB, so ordinary transformations returned exit 1 with no output: seq 1 100000 | sed 's/$/<20 chars>/' > /out.txt # 0 bytes, exit 1 Drop the destination cap and keep the BudgetedString lease against max_live_intermediate_bytes, which is the limit that actually bounds transient growth inside the builtin (the same layer awk, jq and zip use). The r-amplification attack is still refused at the budget; 2.6 MB of ordinary sed output through a pipe or into a file works again. Renumber the threat row to TM-DOS-112: TM-DOS-109 is already assigned to silent truncation at builtin caps. Claude-Session: https://claude.ai/code/session_01MJBT5na4uL5yZZXwwH1FMy
2f7124e to
2c77530
Compare
|
Reviewed, rebased onto latest 1. The destination cap was the wrong limit (blocking)
Measured on this branch as pushed, versus
Both are ordinary transformations, and the file-redirect case silently left an empty file while the script carried on — data loss, not a bounded refusal. Fix: dropped the destination cap and kept the The amplification this PR set out to stop is still refused: …and the 2.6 MB cases above now match 2. Duplicate threat ID (blocking)The new row was numbered 3. Tests
Validation
Generated by Claude Code |
The spec-vs-public parity test requires every knowledge/ TM ID to appear in crates/bashkit/docs/threat-model.md. Claude-Session: https://claude.ai/code/session_01MJBT5na4uL5yZZXwwH1FMy
Motivation
r/Rimplementation could append a preloaded file/stdin after every selected input line and materialize quadratic output in an unboundedString, allowing attacker-controlled stdin and script text to exhaust host memory synchronously.SED_MAX_CYCLE_STEPSand post-returnmax_stdout_byteschecks did not prevent large transient allocations inside the sed engine.Description
Stringsinks with a budget-awareSinkthat usesBudgetedString, tracks an optionalmax_bytesdestination limit, and records aLimitExceedederror on overrun so further accumulation stops early.Machine::run_segmentaccept an optionalmax_bytesdestination budget and returnResult<String, LimitExceeded>so callers can stop execution on allocation errors instead of materializing huge results.ExecutionLimits::max_stdout_bytesand the sharedExecutionBudgetfromContext, compute per-destination remaining bytes for the currentstdoutaccumulator, and pass that torun_segment; file/in-place sinks lease from the live-intermediate budget instead.write_filessinks for errors before writing to the VFS.sed_resource_teststhat verify repeatedr /dev/stdinis stopped under a smallmax_stdout_byteswhile below-limit output remains unchanged, and update the threat model withTM-DOS-109describing the mitigation.Testing
cargo test -p bashkit sed::tests --lib(passed).cargo test -p bashkit --test integration sed_resource_tests(2 tests passed).cargo fmt --all -- --checkandcargo clippy -p bashkit --lib --tests -- -D warnings(both passed).just check-okfandgit diff --check(passed).Codex Task