A defensive mitigation I contributed to rtk-ai/rtk (71k★), closing a hook-hijacking / command-injection surface in the agent's auto-approve pipeline. Merged as PR #119 after the project's security review.
RTK installs a PreToolUse hook (~/.claude/hooks/rtk-rewrite.sh) that rewrites and
auto-approves commands (permissionDecision: "allow") — bypassing the agent's
permission prompt on every call. The hook is a plain user-owned script. Any code
already running as that user (a malicious npm postinstall, a compromised transitive
dependency, local malware) can overwrite it and have arbitrary commands run silently,
under the agent's own trust.
Same class as PromptArmor's marketplace-plugin hook hijacking (Oct 2025) and the Cymulate Claude Code injection vectors (CVE-2025-54794 / CVE-2025-54795, Aug 2025).
A fail-closed integrity gate around the hook:
src/integrity.rs(~525 LOC) —compute_hash/store_hash/verify_hook/runtime_check, a five-state machine (Verified·Tampered·NoBaseline·NotInstalled·OrphanedHash), 14 unit tests across every state and edge.- Baseline written on
rtk initas asha256sum-compatible.rtk-hook.sha256, set read-only (0o444) — a speed bump forcing an attacker tochmodfirst. - Runtime gate — operational commands (
git,cargo, …) runruntime_checkat startup; meta commands (init,verify, …) skip it. Tampered hash → stderr warning and refusal to execute. EmergencyRTK_SKIP_INTEGRITY=1override. rtk verify— manual check surfacing PASS/FAIL with both hashes.
- Not absolute: an attacker who rewrites both hook and hash file defeats it — defense-in-depth that raises cost, not a cryptographic guarantee.
- No signing yet (would require key management).
- Bash self-verification is bypassable, so the check lives in the Rust binary, not the script.
- Merged PR — rtk-ai/rtk#119
- Touched —
src/integrity.rs,src/init.rs,src/main.rs,Cargo.toml