fix(interpreter): meter resource usage for $(<file) command substitutions - #2469
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 14f036e | Commit Preview URL | Sep 25 2026, 05:29 PM |
The budget charge for a command substitution hand-duplicated the escape set from quote_expansion_for_quoted_glob purely to predict how many bytes append_expansion_for_word would write. The two agreed, but only by coincidence of being edited together: adding a metacharacter to the quoting function would silently under-charge the live-byte lease and stop it bounding the substitution. Extract needs_glob_escape as the one definition, derive both the quoting and the byte count from it, and add a test asserting the charge equals what append actually writes across quoted/glob combinations, metacharacter runs, and multi-byte input. Renumber the threat row to TM-DOS-115: TM-DOS-111 is now assigned to silent scalar assignment rejection (#2466), and 112-114 are taken. Claude-Session: https://claude.ai/code/session_01MJBT5na4uL5yZZXwwH1FMy
f1f33d3 to
14f036e
Compare
|
Reviewed, rebased onto latest 1. Duplicate threat ID (blocking)The new row was numbered The threat-model conflicts from the rebase resolved to 2. The byte charge duplicated the escape set (blocking)
let appended_bytes = if word.quoted && word.has_unquoted_glob {
trimmed.chars().map(|ch| if matches!(ch, '\\' | '*' | '?' | ... ) { ch.len_utf8() + 1 } ...The two sets do agree today — I diffed them character by character. But they agree only by having been written together. Add a metacharacter to the quoting function and this charge silently under-counts, and a silent under-charge in live-byte accounting is exactly the bound this PR exists to enforce. Nothing would fail; the lease would just stop covering the bytes. Fix: extracted Then pinned the invariant so it cannot regress silently: assert_eq!(
Interpreter::expansion_appended_len(&w, value),
appended.len(), // from the real append_expansion_for_word
);across both Validation
Generated by Claude Code |
Motivation
$(<file)path read files directly and bypassedexecute_command, skippingexecution_budget.consume_work(1),counters.tick_command, session counters, and live-intermediate byte accounting and enabling repeated file reads to evade limits and inflate memory.Description
charge_command_execution()that callsexecution_budget.consume_work(1),counters.tick_command(...), and session limit checks, and use it at the normal command entry and in the optimized$(<file)fast path (interpreter:charge_command_execution,execute_cmd_subst).Stringgrowth in command-substitution withBudgetedStringand acquireExecutionBudgetlive-byte leases for file contents and substitution text so allocations are charged tolease_byteswhile text is live (files:process_input_redirectionsusage; cmdsubst & expansion:BudgetedString,try_push_str,lease_bytes).expand_word_inner).execution_budget_tests::command_substitution_file_read_*to assert per-exec command limits, session-command limits, work-unit limits, and live-intermediate byte limits reject the crafted workloads, and update the threat model docs withTM-DOS-111and knowledge/log entries.Testing
cargo test -p bashkit --test integration execution_budget_tests::command_substitution_file_read -- --nocaptureand the fullexecution_budget_testssuite, and all new and affected tests passed.cargo test -p bashkit --test integration threat_model_doc_tests::public_threat_model_doc_covers_every_spec_threat_id,cargo clippy -p bashkit --tests -- -D warnings,cargo test -p bashkit --test integration, andjust pre-pr; after installingPyYAMLfor the environment, these checks completed successfully.cargo bench -p bashkit --bench parallel_execution -- --noplotandjust check-okf; benchmarks and OKF checks completed without regressions.Codex Task