* feat: add `rtk rewrite` command — single source of truth for hook rewrites Implements `rtk rewrite <cmd>` as the canonical rewrite engine for all LLM hook integrations (Claude Code, Gemini CLI, future tools). - Add `rewrite_prefixes` field to `RtkRule` in discover/registry.rs - Add public `rewrite_command()` with compound command support (&&, ||, ;, |) - Add `rewrite_segment()`, `rewrite_compound()`, `strip_word_prefix()` helpers - Handle already-rtk commands (exit 0, identical output) - Handle unsupported/ignored commands (exit 1, no output) - Add 20 unit tests covering all branches and edge cases - Create `src/rewrite_cmd.rs` thin CLI wrapper - Register `Commands::Rewrite` in main.rs - Simplify `.claude/hooks/rtk-rewrite.sh` from 357 → 60 lines Hooks no longer need duplicate mapping logic — a single `REWRITTEN=$(rtk rewrite "$CMD") || exit 0` handles everything. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: fix version references and module count for 0.22.2 - README.md, CLAUDE.md, ARCHITECTURE.md: 0.20.1 → 0.22.2 - ARCHITECTURE.md: module count 48 → 51 (added rewrite_cmd + 2 from master) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: update hook coverage check to verify registry (not hook script) Since PR #241, the hook delegates to `rtk rewrite` — command mappings live in src/discover/registry.rs, not the bash hook script. Update the "Verify hook coverage" CI step to: - Check that the hook calls `rtk rewrite` (new architecture) - Check that registry.rs has rewrite_prefixes for all Python/Go commands Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: support single `&` background operator in compound rewrites Per feedback from @xDelph: AI agents increasingly use `cmd1 & cmd2` for parallel execution. This commit adds support alongside existing `&&`, `||`, `;`, and `|` operators. Changes: - rewrite_compound: add match arm for single `&` (after `&&` check) - rewrite_command: add `" & "` to has_compound detection - init: show "installed/updated" vs "already up to date" so users know whether rtk init changed the hook on re-run Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: add Python/Go commands to rewrite registry Add `ruff`, `pytest`, `pip`, `go`, and `golangci-lint` to both PATTERNS and RULES in registry.rs so the CI coverage check passes and `rtk rewrite` correctly identifies these commands. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update version reference to 0.23.0 in README Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update version reference to 0.23.0 in CLAUDE.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update version and module count to 0.23.0/52 in ARCHITECTURE.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address P0 review feedback on rtk-rewrite PR P0.1 - Replace 218-line bash hook with 58-line thin delegating hook. All rewrite logic now lives exclusively in `rtk rewrite` (Rust registry). `rtk init` installs the thin hook via include_str!. P0.2 - Fix `head -20 file` crash at runtime. Generic prefix replacement produced `rtk read -20 file` (invalid clap args). Now translates `head -N file` → `rtk read file --max-lines N` and skips unsupported head flags (e.g. -c) by returning exit 1. P0.3 - Add version guard in hook for rtk < 0.23.0. Prints a warning to stderr instead of silently doing nothing. Also adds missing registry entries vs old hook: - gh release, cargo install - docker run/exec/build, kubectl describe/apply - tree, diff 474 tests pass, 0 clippy warnings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * style: cargo fmt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(discover): extract rules/patterns into rules.rs Per aeppling review: registry.rs was too large (~1500 lines). Extract all static data into src/discover/rules.rs: - RtkRule struct (with pub fields) - PATTERNS const array - RULES const array - IGNORED_PREFIXES and IGNORED_EXACT const arrays registry.rs now contains only logic + tests. rules.rs is the single place to add a new command mapping. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update version reference to 0.24.0 in README * docs: update version references to 0.24.0 in CLAUDE.md and ARCHITECTURE.md * fix(discover): add aws and psql to rewrite registry rtk aws and rtk psql modules exist since PR #216 but were missing from the registry rules — rewrite was silently skipping them. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(discover): exhaustive regression tests for rewrite registry Cover all command categories added in PR #241 and missing from the previous test suite: - aws / psql (PR #216 modules, gap detected during non-reg run) - Python: ruff, pytest, python -m pytest, pip, uv pip - Go: go test/build/vet, golangci-lint - JS/TS: vitest, pnpm vitest, prisma, prettier, pnpm list - Compound operators: || and ; rewrites, 4-segment chains, mixed supported/unsupported segments, all-unsupported → None - sudo prefix rewrite, env var prefix rewrite - find with native flags - Registry invariants: PATTERNS/RULES aligned, all rules valid, all patterns are valid regex Before: 559 tests. After: 607 tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(migration): document breaking change + detect outdated hook in rtk config CHANGELOG [Unreleased]: - Migration required: rtk init --global needed after upgrade - Documents upgrade path and explains no immediate breakage rtk init --show / rtk config now detects old hook (inline if-else) vs new thin delegator (rtk rewrite) and prints actionable warning: ⚠️ Hook: ~/.claude/hooks/rtk-rewrite.sh (outdated — inline logic, not thin delegator) → Run `rtk init --global` to upgrade to the single source of truth hook Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update hook references for thin delegator (0.24.0) - ARCHITECTURE.md: fix hook description (shell script → thin delegator) - INSTALL.md: add upgrade path for old hook users (pre-0.24 breaking change) - SECURITY.md: add registry.rs + hook files to Tier 1 critical files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(arch): fix module count 52→55, add aws/psql/rewrite to module map Adds aws_cmd, psql_cmd (PR #216) and rewrite_cmd (this PR) to the Complete Module Map table. Updates total from 52 to 55 to match main.rs. Fixes CI validate-docs.sh module count mismatch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: fix hook coverage check to search rules.rs instead of registry.rs After the PR #241 refactor, rewrite_prefixes constants moved from registry.rs to rules.rs. Update grep to search the whole discover/ directory so it finds them in the right file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
7.3 KiB
Security Policy
Reporting a Vulnerability
If you discover a security vulnerability in RTK, please report it to the maintainers privately:
- Email: security@rtk-ai.dev (or create a private security advisory on GitHub)
- Response time: We aim to acknowledge reports within 48 hours
- Disclosure: We follow responsible disclosure practices (90-day embargo)
Please do NOT:
- Open public GitHub issues for security vulnerabilities
- Disclose vulnerabilities on social media or forums before we've had a chance to address them
Security Review Process for Pull Requests
RTK is a CLI tool that executes shell commands and handles user input. PRs from external contributors undergo enhanced security review to protect against:
- Shell injection (command execution vulnerabilities)
- Supply chain attacks (malicious dependencies)
- Backdoors (logic bombs, exfiltration code)
- Data leaks (tracking.db exposure, telemetry abuse)
Automated Security Checks
Every PR triggers our security-check.yml workflow:
- Dependency audit (
cargo audit) - Detects known CVEs - Critical files alert - Flags modifications to high-risk files
- Dangerous pattern scan - Regex-based detection of:
- Shell execution (
Command::new("sh")) - Environment manipulation (
.env("LD_PRELOAD")) - Network operations (
reqwest::,std::net::) - Unsafe code blocks
- Panic-inducing patterns (
.unwrap()in production)
- Shell execution (
- Clippy security lints - Enforces Rust best practices
Results are posted in the PR's GitHub Actions summary.
Critical Files Requiring Enhanced Review
The following files are considered high-risk and trigger mandatory 2-reviewer approval:
Tier 1: Shell Execution & System Interaction
src/runner.rs- Shell command execution engine (primary injection vector)src/summary.rs- Command output aggregation (data exfiltration risk)src/tracking.rs- SQLite database operations (privacy/telemetry concerns)src/discover/registry.rs- Rewrite logic for all commands (command injection risk via rewrite rules)hooks/rtk-rewrite.sh/.claude/hooks/rtk-rewrite.sh- Thin delegator hook (executes in Claude Code context, intercepts all commands)
Tier 2: Input Validation
src/pnpm_cmd.rs- Package name validation (prevents injection via malicious names)src/container.rs- Docker/container operations (privilege escalation risk)
Tier 3: Supply Chain & CI/CD
Cargo.toml- Dependency manifest (typosquatting, backdoored crates).github/workflows/*.yml- CI/CD pipelines (release tampering, secret exfiltration)
If your PR modifies ANY of these files, expect:
- Detailed manual security review
- Request for clarification on design choices
- Potentially slower merge timeline
Review Workflow
For External Contributors
- Submit PR → Automated
security-check.ymlruns - Review automated results → Fix any flagged issues
- Manual review → Maintainer performs comprehensive security audit
- Approval → Merge (or request for changes)
For Maintainers
Use the comprehensive security review process:
# If Claude Code available, run the dedicated skill:
/rtk-pr-security <PR_NUMBER>
# Manual review (without Claude):
gh pr view <PR_NUMBER>
gh pr diff <PR_NUMBER> > /tmp/pr.diff
bash scripts/detect-dangerous-patterns.sh /tmp/pr.diff
Review checklist:
- No critical files modified OR changes justified + reviewed by 2 maintainers
- No dangerous patterns OR patterns explained + safe
- No new dependencies OR deps audited on crates.io (downloads, maintainer, license)
- PR description matches actual code changes (intent vs reality)
- No logic bombs (time-based triggers, conditional backdoors)
- Code quality acceptable (no unexplained complexity spikes)
Dangerous Patterns We Check For
| Pattern | Risk | Example |
|---|---|---|
Command::new("sh") |
Shell injection | Spawns shell with user input |
.env("LD_PRELOAD") |
Library hijacking | Preloads malicious shared libraries |
reqwest::, std::net:: |
Data exfiltration | Unexpected network operations |
unsafe { |
Memory safety | Bypasses Rust's guarantees |
.unwrap() in src/ |
DoS via panic | Crashes on invalid input |
SystemTime::now() > ... |
Logic bombs | Delayed malicious behavior |
| Base64/hex strings | Obfuscation | Hides malicious URLs/commands |
See Dangerous Patterns Reference for exploitation examples.
Dependency Security
New dependencies added to Cargo.toml must meet these criteria:
- Downloads: >10,000 on crates.io (or strong justification if lower)
- Maintainer: Verified GitHub profile + track record of other crates
- License: MIT or Apache-2.0 compatible
- Activity: Recent commits (within 6 months)
- No typosquatting: Manual verification against similar crate names
Red flags:
- Brand new crate (<1 month old) with low downloads
- Anonymous maintainer with no GitHub history
- Crate name suspiciously similar to popular crate (e.g.,
seridvsserde) - License change in recent versions
Security Best Practices for Contributors
Avoid These Anti-Patterns
❌ DON'T:
// Shell injection risk
let user_input = get_arg();
Command::new("sh").arg("-c").arg(format!("echo {}", user_input)).output();
// Panic on invalid input
let path = std::env::args().nth(1).unwrap();
// Hardcoded secrets
const API_KEY: &str = "sk_live_1234567890abcdef";
✅ DO:
// No shell, direct binary execution
let user_input = get_arg();
Command::new("echo").arg(user_input).output();
// Graceful error handling
let path = std::env::args().nth(1).context("Missing path argument")?;
// Env vars or config files for secrets
let api_key = std::env::var("API_KEY").context("API_KEY not set")?;
Error Handling Guidelines
- Use
anyhow::Result<T>with.context()for all error propagation - NEVER use
.unwrap()insrc/(tests are OK) - Prefer
.expect("descriptive message")over.unwrap()if unavoidable - Use
?operator instead ofunwrap()for propagation
Input Validation
- Validate all user input before passing to
Command - Use allowlists for command flags (not denylists)
- Canonicalize file paths to prevent traversal attacks
- Sanitize package names with strict regex patterns
Disclosure Timeline
When vulnerabilities are reported:
- Day 0: Acknowledgment sent to reporter
- Day 7: Maintainers assess severity and impact
- Day 14: Patch development begins
- Day 30: Patch released + CVE filed (if applicable)
- Day 90: Public disclosure (or earlier if patch is deployed)
Critical vulnerabilities (remote code execution, data exfiltration) may be fast-tracked.
Security Tooling
cargo audit- Automated CVE scanning (runs in CI)cargo deny- License compliance + banned dependenciescargo clippy- Lints for unsafe patterns- GitHub Dependabot - Automated dependency updates
- GitHub Code Scanning - Static analysis via CodeQL (planned)
Contact
- Security issues: security@rtk-ai.dev
- General questions: https://github.com/rtk-ai/rtk/discussions
- Maintainers: @FlorianBruniaux (active fork maintainer)
Last updated: 2026-03-05