fix(security): SAST audit — shell injection, exit codes, lazy regex + telemetry default#965
Closed
celstnblacc wants to merge 7 commits intortk-ai:masterfrom
Closed
fix(security): SAST audit — shell injection, exit codes, lazy regex + telemetry default#965celstnblacc wants to merge 7 commits intortk-ai:masterfrom
celstnblacc wants to merge 7 commits intortk-ai:masterfrom
Conversation
When RTK is invoked via Claude Code's PreToolUse hook, the hook keeps its stdin pipe open for the duration of the session. rg/grep subprocesses inherit this open pipe and block waiting for EOF — they never terminate. On macOS with multiple concurrent grep calls this causes memory to accumulate unboundedly, eventually triggering a kernel panic (observed: ~514GB RAM+swap consumed, single process at ~198GB). Fix: redirect subprocess stdin to /dev/null so children are decoupled from the hook pipe and terminate normally after completing their search. Fixes rtk-ai#897 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… to worktree root (rtk-ai#892) fix rtk-ai#886 — permission bypass: RTK was ignoring Claude Code's permissions.allow list and defaulting to Allow for any command not explicitly denied. Now loads the allow list from settings.json alongside deny/ask rules. When an allow list is present, commands not matching any allow pattern return Ask instead of Allow, matching Claude Code's documented whitelist-by-default behaviour. Fully backward-compatible: no allow list configured → existing Allow behaviour unchanged. fix rtk-ai#892 — worktree RTK.md path: rtk init --codex in local (non-global) mode was writing AGENTS.md and RTK.md as bare relative paths (PathBuf::from("RTK.md")), anchored to the CWD at init time. When Codex runs from a git worktree the files are not present and sed errors out. Fix: resolve paths via git rev-parse --show-toplevel (worktree-aware) so files are always placed at the worktree root. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
P0 — resolved_command() now sets stdin to Stdio::null() by default. This fixes the rtk-ai#897 stdin leak across ALL 128+ subprocess call sites, not just grep_cmd.rs. Commands needing stdin can override explicitly. P1 — Removed telemetry entirely. Deleted telemetry.rs (339 lines), removed ureq/hostname/getrandom deps, cleaned dead code in config.rs, tracking.rs, init.rs. Local tracking (rtk gain) is preserved. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace eval with bash -c in benchmark.sh (SHELL-001 CRITICAL x 3) - Add changelog entry for fork security patches (v0.34.2-security.1) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…telemetry default C-1: replace sh -c exec with shell_words::split + Command::new(bin).args(rest) in runner.rs C-2: propagate child exit codes via exit_code_from_output/exit_code_from_status throughout H-1: compile summary.rs regexes once at startup via lazy_static! H-2: fix divide-by-zero panic in cc_economics savings_blended calculation H-3: replace infallible stash subcommand unwrap with Some(sub @ (...)) pattern M-1: TelemetryConfig::default() now sets enabled: false (no opt-in required) M-2: CWD fallback in resolved_command uses "." instead of empty path M-3: remove grouped.get().unwrap() panic path in report.rs with let-else L-2: migrate all status.code().unwrap_or(1) to exit_code_from_status across git/main Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
newblacc seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
Author
|
Closing — this is a fork-internal security audit for celstnblacc/rtk, not intended for upstream. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Full SAST audit of the
celstnblacc/rtkfork. All findings are in code we own — no upstream changes involved.Critical / High
C-1 — Shell injection via
sh -c:run_err/run_testinrunner.rspassed the command string directly toCommand::new("sh").args(["-c", command]). A semicolon in a test command name (e.g.make test; rm -rf /) would spawn extra shell processes. Fixed withshell_words::split()+Command::new(bin).args(rest)— metacharacters are now literal arguments, not shell syntax.C-2 — Exit code not propagated:
run_err/run_testreturnedOk(())even when the child process exited non-zero, causing CI to silently pass on failure. Fixed withexit_code_from_output()→std::process::exit(code). Signal-killed processes return128 + signalper Unix convention.High
H-1 —
Regex::new()inside function:summary.rs::extract_number()calledRegex::new()on every invocation. Migrated tolazy_static!with one static per pattern (RE_PASSED,RE_FAILED,RE_SKIPPED,RE_IGNORED).H-2 — Divide-by-zero panic:
cc_economics.rscomputedsavings_blendedby multiplyingrtk_saved_tokensbytotals.blended_cpt.unwrap()beforeblended_cptwas set. Reordered to deriveblended_cpt = cc_cost / cc_total_tokensfirst, then assign both fields.H-3 — Infallible
unwrap()on stash subcommand:git.rsmatchedSome("pop") | Some("apply") | ...then immediately calledsubcommand.unwrap(). Replaced withSome(sub @ ("pop" | "apply" | "drop" | "push"))binding pattern — no unwrap needed.Medium
M-1 — Telemetry on by default:
TelemetryConfig::default()setenabled: true. This fork has stripped telemetry; the default now reflects that (enabled: false).M-2 — CWD fallback produces empty path:
resolved_commandusedunwrap_or_default()for the CWD fallback, which returns an emptyPathBuf. Changed tounwrap_or_else(|_| PathBuf::from(".")).M-3 —
unwrap()onHashMap::getinreport.rs:grouped.get(&base_cmd).unwrap()would panic if a key appeared in the iterator but not the map (possible under concurrent modification). Replaced withlet Some(rules) = grouped.get(&base_cmd) else { continue }.Low
status.code().unwrap_or(1)throughout: All remainingExitStatus::code().unwrap_or(1)calls ingh_cmd.rs,gt_cmd.rs,git.rs, andmain.rsmigrated toexit_code_from_status()— returns128 + signalon signal termination instead of1.Test plan
cargo test --all— 1152 tests passing (5 ignored integration tests)cargo clippy --all-targets— zero warningscargo build --release— clean buildtest_split_command_semicolon_is_literal,test_split_exec_blocks_semicolon_injection,test_run_err_propagates_exit_code(#[ignore] — integration)🤖 Generated with Claude Code