[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6C55ZXSNWBB861V5GGYR775":3,"skill-same:01M6C55ZXSNWBB861V5GGYR775":56},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":29,"linkOnly":30,"markdownContent":31,"markdownBody":32,"frontMatter":33,"refName":35,"commitSha":36,"skillDirectory":37,"forksCount":38,"repositoryHtmlUrl":39,"repositoryHomepageUrl":6,"ownerHtmlUrl":40,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":41,"install":42,"resources":55},"01M6C55ZXSNWBB861V5GGYR775","security-guardian",null,"CLI security expert for RTK - command injection, shell escaping, hook security","MDMAtk\u002FTormentNexus","MDMAtk","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F288684999?v=4","01M6C54H4AG0BYB3HTFA4C0CBJ","01M6C54H8146YDG94N9KWXV521",13,"AGPL-3.0","https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Fsecurity-guardian\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28],"security","guardian","security guardian","cli","injection","shell","Bash","grep","command","read","rtk","2026-11-10 10:12:30",false,"---\ndescription: CLI security expert for RTK - command injection, shell escaping, hook security\nallowed-tools: Read Grep Glob Bash\n---\n\n# Security Guardian\n\nComprehensive security analysis for RTK CLI tool, focusing on **command injection**, **shell escaping**, **hook security**, and **malicious input handling**.\n\n## When to Use\n\n- **Automatically triggered**: After filter changes, shell command execution logic, hook modifications\n- **Manual invocation**: Before release, after security-sensitive code changes\n- **Proactive**: When handling user input, executing shell commands, or parsing untrusted output\n\n## RTK Security Threat Model\n\nRTK faces unique security challenges as a CLI proxy that:\n1. **Executes shell commands** based on user input\n2. **Parses untrusted command output** (git, cargo, gh, etc.)\n3. **Integrates with Claude Code hooks** (rtk-rewrite.sh, rtk-suggest.sh)\n4. **Routes commands transparently** (command injection vectors)\n\n### Threat Categories\n\n| Threat | Severity | Impact | Mitigation |\n|--------|----------|--------|------------|\n| **Command Injection** | ? CRITICAL | Remote code execution | Input validation, shell escaping |\n| **Shell Escaping** | ? CRITICAL | Arbitrary command execution | Platform-specific escaping |\n| **Hook Injection** | ? HIGH | Hook hijacking, command interception | Permission checks, signature validation |\n| **Malicious Output** | ? MEDIUM | RTK crash, DoS | Robust parsing, error handling |\n| **Path Traversal** | ? LOW | File access outside filters\u002F | Path sanitization |\n\n## Security Analysis Workflow\n\n### 1. Threat Identification\n\n**Questions to ask** for every code change:\n\n```\nInput Validation:\n- Does this code accept user input?\n- Is the input validated before use?\n- Can special characters (;, |, &, $, `, \\, etc.) cause issues?\n\nShell Execution:\n- Does this code execute shell commands?\n- Are command arguments properly escaped?\n- Is std::process::Command used (safe) or shell=true (dangerous)?\n\nOutput Parsing:\n- Does this code parse external command output?\n- Can malformed output cause panics or crashes?\n- Are regex patterns tested against malicious input?\n\nHook Integration:\n- Does this code modify hooks?\n- Are hook permissions validated (executable bit)?\n- Is hook source code integrity checked?\n```\n\n### 2. Code Audit Patterns\n\n**Command Injection Detection**:\n\n```rust\n\u002F\u002F ? CRITICAL: Shell injection vulnerability\nlet user_input = env::args().nth(1).unwrap();\nlet cmd = format!(\"git log {}\", user_input); \u002F\u002F DANGEROUS!\nstd::process::Command::new(\"sh\")\n    .arg(\"-c\")\n    .arg(&cmd) \u002F\u002F Attacker can inject: `; rm -rf \u002F`\n    .spawn();\n\n\u002F\u002F ✅ SAFE: Use Command builder, not shell\nuse std::process::Command;\n\nlet user_input = env::args().nth(1).unwrap();\nCommand::new(\"git\")\n    .arg(\"log\")\n    .arg(&user_input) \u002F\u002F Safely passed as argument, not interpreted by shell\n    .spawn();\n```\n\n**Shell Escaping Vulnerability**:\n\n```rust\n\u002F\u002F ? CRITICAL: No escaping for special chars\nfn execute_raw(cmd: &str, args: &[&str]) -> Result\u003COutput> {\n    let full_cmd = format!(\"{} {}\", cmd, args.join(\" \"));\n    Command::new(\"sh\")\n        .arg(\"-c\")\n        .arg(&full_cmd) \u002F\u002F DANGEROUS: args not escaped\n        .output()\n}\n\n\u002F\u002F ✅ SAFE: Use Command builder, automatic escaping\nfn execute_raw(cmd: &str, args: &[&str]) -> Result\u003COutput> {\n    Command::new(cmd)\n        .args(args) \u002F\u002F Safely escaped by Command API\n        .output()\n}\n```\n\n**Malicious Output Handling**:\n\n```rust\n\u002F\u002F ? CRITICAL: Panic on unexpected output\nfn filter_git_log(input: &str) -> String {\n    let first_line = input.lines().next().unwrap(); \u002F\u002F Panic if empty!\n    let hash = &first_line[7..47]; \u002F\u002F Panic if line too short!\n    hash.to_string()\n}\n\n\u002F\u002F ✅ SAFE: Graceful error handling\nfn filter_git_log(input: &str) -> Result\u003CString> {\n    let first_line = input.lines().next()\n        .ok_or_else(|| anyhow::anyhow!(\"Empty input\"))?;\n\n    if first_line.len() \u003C 47 {\n        bail!(\"Invalid git log format\");\n    }\n\n    Ok(first_line[7..47].to_string())\n}\n```\n\n**Hook Injection Prevention**:\n\n```bash\n# ? CRITICAL: Hook not checking source\n#!\u002Fbin\u002Fbash\n# rtk-rewrite.sh\n\n# Execute command without validation\neval \"$CLAUDE_CODE_HOOK_BASH_TEMPLATE\" # DANGEROUS!\n\n# ✅ SAFE: Validate hook environment\n#!\u002Fbin\u002Fbash\n# rtk-rewrite.sh\n\n# Verify running in Claude Code context\nif [ -z \"$CLAUDE_CODE_HOOK_BASH_TEMPLATE\" ]; then\n    echo \"Error: Not running in Claude Code context\"\n    exit 1\nfi\n\n# Validate RTK binary exists and is executable\nif ! command -v rtk >\u002Fdev\u002Fnull 2>&1; then\n    echo \"Error: rtk binary not found\"\n    exit 1\nfi\n\n# Execute with explicit path (no PATH hijacking)\n\u002Fusr\u002Flocal\u002Fbin\u002Frtk \"$@\"\n```\n\n### 3. Security Testing\n\n**Command Injection Tests**:\n\n```rust\n#[cfg(test)]\nmod security_tests {\n    use super::*;\n\n    #[test]\n    fn test_command_injection_defense() {\n        \u002F\u002F Malicious input: attempt shell injection\n        let malicious_inputs = vec![\n            \"; rm -rf \u002F\",\n            \"| cat \u002Fetc\u002Fpasswd\",\n            \"$(whoami)\",\n            \"`id`\",\n            \"&& curl evil.com\",\n        ];\n\n        for input in malicious_inputs {\n            \u002F\u002F Should NOT execute injected commands\n            let result = execute_command(\"git\", &[\"log\", input]);\n\n            \u002F\u002F Either:\n            \u002F\u002F 1. Returns error (command fails safely), OR\n            \u002F\u002F 2. Treats input as literal string (no shell interpretation)\n            \u002F\u002F Both acceptable - just don't execute injection!\n        }\n    }\n\n    #[test]\n    fn test_shell_escaping() {\n        \u002F\u002F Special characters that need escaping\n        let special_chars = vec![\n            \";\", \"|\", \"&\", \"$\", \"`\", \"\\\\\", \"\\\"\", \"'\", \"\\n\", \"\\r\",\n        ];\n\n        for char in special_chars {\n            let arg = format!(\"test{}value\", char);\n            let escaped = escape_for_shell(&arg);\n\n            \u002F\u002F Escaped version should NOT be interpreted by shell\n            assert!(!escaped.contains(char) || escaped.contains('\\\\'));\n        }\n    }\n}\n```\n\n**Malicious Output Tests**:\n\n```rust\n#[test]\nfn test_malicious_output_handling() {\n    \u002F\u002F Malformed outputs that could crash RTK\n    let malicious_outputs = vec![\n        \"\", \u002F\u002F Empty\n        \"\\n\\n\\n\", \u002F\u002F Only newlines\n        \"x\".repeat(1_000_000), \u002F\u002F 1MB of 'x' (memory exhaustion)\n        \"\\x00\\x01\\x02\", \u002F\u002F Binary data\n        \"\\u{FFFD}\".repeat(1000), \u002F\u002F Unicode replacement chars\n    ];\n\n    for output in malicious_outputs {\n        let result = filter_git_log(&output);\n\n        \u002F\u002F Should either:\n        \u002F\u002F 1. Return Ok with filtered output, OR\n        \u002F\u002F 2. Return Err (graceful failure)\n        \u002F\u002F Both acceptable - just don't panic!\n        assert!(result.is_ok() || result.is_err());\n    }\n}\n```\n\n## Security Vulnerabilities Checklist\n\n### Command Injection (? Critical)\n\n- [ ] **No shell=true**: Never use `.arg(\"-c\")` with user input\n- [ ] **Command builder**: Use `std::process::Command` API (not shell strings)\n- [ ] **Input validation**: Validate\u002Fsanitize before command execution\n- [ ] **Whitelist approach**: Only allow known-safe commands\n\n**Detection**:\n```bash\n# Find dangerous shell execution\nrg \"\\.arg\\(\\\"-c\\\"\\)\" --type rust src\u002F\nrg \"std::process::Command::new\\(\\\"sh\\\"\\)\" --type rust src\u002F\nrg \"format!.*\\{.*Command\" --type rust src\u002F\n```\n\n### Shell Escaping (? Critical)\n\n- [ ] **Platform-specific**: Test escaping on macOS, Linux, Windows\n- [ ] **Special chars**: Handle `;`, `|`, `&`, `$`, `` ` ``, `\\`, `\"`, `'`, `\\n`\n- [ ] **Use shell-escape crate**: Don't roll your own escaping\n- [ ] **Cross-platform tests**: `#[cfg(target_os = \"...\")]` tests\n\n**Detection**:\n```bash\n# Find potential escaping issues\nrg \"format!.*\\{.*args\" --type rust src\u002F\nrg \"\\.join\\(\\\" \\\"\\)\" --type rust src\u002F\n```\n\n### Hook Security (? High)\n\n- [ ] **Permission checks**: Verify hooks are executable (`-rwxr-xr-x`)\n- [ ] **Source validation**: Only execute hooks from `.claude\u002Fhooks\u002F`\n- [ ] **Environment validation**: Check `$CLAUDE_CODE_HOOK_BASH_TEMPLATE`\n- [ ] **No dynamic evaluation**: No `eval` or `source` of untrusted files\n\n**Hook security checklist**:\n```bash\n#!\u002Fbin\u002Fbash\n# rtk-rewrite.sh\n\n# 1. Verify Claude Code context\nif [ -z \"$CLAUDE_CODE_HOOK_BASH_TEMPLATE\" ]; then\n    exit 1\nfi\n\n# 2. Verify RTK binary exists\nif ! command -v rtk >\u002Fdev\u002Fnull 2>&1; then\n    exit 1\nfi\n\n# 3. Use absolute path (prevent PATH hijacking)\nRTK_BIN=$(which rtk)\n\n# 4. Validate RTK version (prevent downgrade attacks)\nif ! \"$RTK_BIN\" --version | grep -q \"rtk 0.16\"; then\n    echo \"Warning: RTK version mismatch\"\nfi\n\n# 5. Execute with explicit path\n\"$RTK_BIN\" \"$@\"\n```\n\n### Malicious Output (? Medium)\n\n- [ ] **No .unwrap()**: Use `Result` for parsing, graceful error handling\n- [ ] **Bounds checking**: Verify string lengths before slicing\n- [ ] **Regex timeouts**: Prevent ReDoS (Regular Expression Denial of Service)\n- [ ] **Memory limits**: Cap output size before parsing\n\n**Parsing safety pattern**:\n```rust\nfn safe_parse(output: &str) -> Result\u003CString> {\n    \u002F\u002F 1. Check output size (prevent memory exhaustion)\n    if output.len() > 10_000_000 {\n        bail!(\"Output too large (>10MB)\");\n    }\n\n    \u002F\u002F 2. Validate format (prevent malformed input)\n    if !output.starts_with(\"commit \") {\n        bail!(\"Invalid git log format\");\n    }\n\n    \u002F\u002F 3. Bounds checking (prevent panics)\n    let first_line = output.lines().next()\n        .ok_or_else(|| anyhow::anyhow!(\"Empty output\"))?;\n\n    if first_line.len() \u003C 47 {\n        bail!(\"Commit hash too short\");\n    }\n\n    \u002F\u002F 4. Safe extraction\n    Ok(first_line[7..47].to_string())\n}\n```\n\n## Security Best Practices\n\n### Input Validation\n\n**Whitelist approach** (safer than blacklist):\n\n```rust\nfn validate_command(cmd: &str) -> Result\u003C()> {\n    \u002F\u002F ✅ SAFE: Whitelist known-safe commands\n    const ALLOWED_COMMANDS: &[&str] = &[\n        \"git\", \"cargo\", \"gh\", \"pnpm\", \"docker\",\n        \"rustc\", \"clippy\", \"rustfmt\",\n    ];\n\n    if !ALLOWED_COMMANDS.contains(&cmd) {\n        bail!(\"Command '{}' not allowed\", cmd);\n    }\n\n    Ok(())\n}\n\n\u002F\u002F ❌ UNSAFE: Blacklist approach (easy to bypass)\nfn validate_command_unsafe(cmd: &str) -> Result\u003C()> {\n    const BLOCKED: &[&str] = &[\"rm\", \"dd\", \"mkfs\"];\n\n    if BLOCKED.contains(&cmd) {\n        bail!(\"Command '{}' blocked\", cmd);\n    }\n\n    Ok(())\n    \u002F\u002F Attacker can use: \u002Fbin\u002Frm, rm.exe, RM (case variation), etc.\n}\n```\n\n### Shell Escaping\n\n**Use dedicated library**:\n\n```rust\nuse shell_escape::escape;\n\nfn escape_arg(arg: &str) -> String {\n    \u002F\u002F ✅ SAFE: Use battle-tested escaping library\n    escape(arg.into()).into()\n}\n\n\u002F\u002F ❌ UNSAFE: Roll your own escaping (likely has bugs)\nfn escape_arg_unsafe(arg: &str) -> String {\n    arg.replace('\"', r#\"\\\"\"#) \u002F\u002F Misses many special chars!\n}\n```\n\n**Platform-specific escaping**:\n\n```rust\n#[cfg(target_os = \"windows\")]\nfn escape_for_shell(arg: &str) -> String {\n    \u002F\u002F PowerShell escaping\n    format!(\"\\\"{}\\\"\", arg.replace('\"', \"`\\\"\"))\n}\n\n#[cfg(not(target_os = \"windows\"))]\nfn escape_for_shell(arg: &str) -> String {\n    \u002F\u002F Bash\u002Fzsh escaping\n    shell_escape::escape(arg.into()).into()\n}\n```\n\n### Secure Command Execution\n\n**Always use Command builder**:\n\n```rust\nuse std::process::Command;\n\n\u002F\u002F ✅ SAFE: Command builder (no shell)\nfn execute_git(args: &[&str]) -> Result\u003COutput> {\n    Command::new(\"git\")\n        .args(args) \u002F\u002F Safely escaped\n        .output()\n        .context(\"Failed to execute git\")\n}\n\n\u002F\u002F ❌ UNSAFE: Shell string concatenation\nfn execute_git_unsafe(args: &[&str]) -> Result\u003COutput> {\n    let cmd = format!(\"git {}\", args.join(\" \"));\n    Command::new(\"sh\")\n        .arg(\"-c\")\n        .arg(&cmd) \u002F\u002F Shell interprets args!\n        .output()\n}\n```\n\n## Security Audit Command Reference\n\n**Find potential vulnerabilities**:\n\n```bash\n# Command injection\nrg \"\\.arg\\(\\\"-c\\\"\\)\" --type rust src\u002F\nrg \"format!.*Command\" --type rust src\u002F\n\n# Shell escaping\nrg \"\\.join\\(\\\" \\\"\\)\" --type rust src\u002F\nrg \"format!.*\\{.*args\" --type rust src\u002F\n\n# Unsafe unwraps (can panic on malicious input)\nrg \"\\.unwrap\\(\\)\" --type rust src\u002F\n\n# Bounds violations\nrg \"\\[.*\\.\\.\\.\\]\" --type rust src\u002F\nrg \"\\[.*\\.\\.]\" --type rust src\u002F\n\n# Hook security\nrg \"eval|source\" --type bash .claude\u002Fhooks\u002F\n```\n\n## Incident Response\n\n**If vulnerability discovered**:\n\n1. **Assess severity**: Use CVSS scoring (Critical\u002FHigh\u002FMedium\u002FLow)\n2. **Develop patch**: Fix vulnerability in isolated branch\n3. **Test fix**: Verify with security tests + integration tests\n4. **Release hotfix**: PATCH version bump (e.g., v0.16.0 → v0.16.1)\n5. **Disclose responsibly**: GitHub Security Advisory, CVE if applicable\n\n**Example advisory template**:\n\n```markdown\n## Security Advisory: Command Injection in rtk v0.16.0\n\n**Severity**: CRITICAL (CVSS 9.8)\n**Affected versions**: v0.15.0 - v0.16.0\n**Fixed in**: v0.16.1\n\n**Description**:\nRTK versions 0.15.0 through 0.16.0 are vulnerable to command injection\nvia malicious git repository names. An attacker can execute arbitrary\nshell commands by creating a repository with special characters in the name.\n\n**Impact**:\nRemote code execution with user privileges.\n\n**Mitigation**:\nUpgrade to v0.16.1 immediately. As a workaround, avoid using RTK in\ndirectories with untrusted repository names.\n\n**Credits**:\nReported by: Security Researcher Name\n```\n\n## Security Resources\n\n**Tools**:\n- `cargo audit` - Dependency vulnerability scanning\n- `cargo-geiger` - Unsafe code detection\n- `cargo-deny` - Dependency policy enforcement\n- `semgrep` - Static analysis for security patterns\n\n**Run security checks**:\n```bash\n# Dependency vulnerabilities\ncargo install cargo-audit\ncargo audit\n\n# Unsafe code detection\ncargo install cargo-geiger\ncargo geiger\n\n# Static analysis\ncargo install semgrep\nsemgrep --config auto\n```\n","# Security Guardian\n\nComprehensive security analysis for RTK CLI tool, focusing on **command injection**, **shell escaping**, **hook security**, and **malicious input handling**.\n\n## When to Use\n\n- **Automatically triggered**: After filter changes, shell command execution logic, hook modifications\n- **Manual invocation**: Before release, after security-sensitive code changes\n- **Proactive**: When handling user input, executing shell commands, or parsing untrusted output\n\n## RTK Security Threat Model\n\nRTK faces unique security challenges as a CLI proxy that:\n1. **Executes shell commands** based on user input\n2. **Parses untrusted command output** (git, cargo, gh, etc.)\n3. **Integrates with Claude Code hooks** (rtk-rewrite.sh, rtk-suggest.sh)\n4. **Routes commands transparently** (command injection vectors)\n\n### Threat Categories\n\n| Threat | Severity | Impact | Mitigation |\n|--------|----------|--------|------------|\n| **Command Injection** | ? CRITICAL | Remote code execution | Input validation, shell escaping |\n| **Shell Escaping** | ? CRITICAL | Arbitrary command execution | Platform-specific escaping |\n| **Hook Injection** | ? HIGH | Hook hijacking, command interception | Permission checks, signature validation |\n| **Malicious Output** | ? MEDIUM | RTK crash, DoS | Robust parsing, error handling |\n| **Path Traversal** | ? LOW | File access outside filters\u002F | Path sanitization |\n\n## Security Analysis Workflow\n\n### 1. Threat Identification\n\n**Questions to ask** for every code change:\n\n```\nInput Validation:\n- Does this code accept user input?\n- Is the input validated before use?\n- Can special characters (;, |, &, $, `, \\, etc.) cause issues?\n\nShell Execution:\n- Does this code execute shell commands?\n- Are command arguments properly escaped?\n- Is std::process::Command used (safe) or shell=true (dangerous)?\n\nOutput Parsing:\n- Does this code parse external command output?\n- Can malformed output cause panics or crashes?\n- Are regex patterns tested against malicious input?\n\nHook Integration:\n- Does this code modify hooks?\n- Are hook permissions validated (executable bit)?\n- Is hook source code integrity checked?\n```\n\n### 2. Code Audit Patterns\n\n**Command Injection Detection**:\n\n```rust\n\u002F\u002F ? CRITICAL: Shell injection vulnerability\nlet user_input = env::args().nth(1).unwrap();\nlet cmd = format!(\"git log {}\", user_input); \u002F\u002F DANGEROUS!\nstd::process::Command::new(\"sh\")\n    .arg(\"-c\")\n    .arg(&cmd) \u002F\u002F Attacker can inject: `; rm -rf \u002F`\n    .spawn();\n\n\u002F\u002F ✅ SAFE: Use Command builder, not shell\nuse std::process::Command;\n\nlet user_input = env::args().nth(1).unwrap();\nCommand::new(\"git\")\n    .arg(\"log\")\n    .arg(&user_input) \u002F\u002F Safely passed as argument, not interpreted by shell\n    .spawn();\n```\n\n**Shell Escaping Vulnerability**:\n\n```rust\n\u002F\u002F ? CRITICAL: No escaping for special chars\nfn execute_raw(cmd: &str, args: &[&str]) -> Result\u003COutput> {\n    let full_cmd = format!(\"{} {}\", cmd, args.join(\" \"));\n    Command::new(\"sh\")\n        .arg(\"-c\")\n        .arg(&full_cmd) \u002F\u002F DANGEROUS: args not escaped\n        .output()\n}\n\n\u002F\u002F ✅ SAFE: Use Command builder, automatic escaping\nfn execute_raw(cmd: &str, args: &[&str]) -> Result\u003COutput> {\n    Command::new(cmd)\n        .args(args) \u002F\u002F Safely escaped by Command API\n        .output()\n}\n```\n\n**Malicious Output Handling**:\n\n```rust\n\u002F\u002F ? CRITICAL: Panic on unexpected output\nfn filter_git_log(input: &str) -> String {\n    let first_line = input.lines().next().unwrap(); \u002F\u002F Panic if empty!\n    let hash = &first_line[7..47]; \u002F\u002F Panic if line too short!\n    hash.to_string()\n}\n\n\u002F\u002F ✅ SAFE: Graceful error handling\nfn filter_git_log(input: &str) -> Result\u003CString> {\n    let first_line = input.lines().next()\n        .ok_or_else(|| anyhow::anyhow!(\"Empty input\"))?;\n\n    if first_line.len() \u003C 47 {\n        bail!(\"Invalid git log format\");\n    }\n\n    Ok(first_line[7..47].to_string())\n}\n```\n\n**Hook Injection Prevention**:\n\n```bash\n# ? CRITICAL: Hook not checking source\n#!\u002Fbin\u002Fbash\n# rtk-rewrite.sh\n\n# Execute command without validation\neval \"$CLAUDE_CODE_HOOK_BASH_TEMPLATE\" # DANGEROUS!\n\n# ✅ SAFE: Validate hook environment\n#!\u002Fbin\u002Fbash\n# rtk-rewrite.sh\n\n# Verify running in Claude Code context\nif [ -z \"$CLAUDE_CODE_HOOK_BASH_TEMPLATE\" ]; then\n    echo \"Error: Not running in Claude Code context\"\n    exit 1\nfi\n\n# Validate RTK binary exists and is executable\nif ! command -v rtk >\u002Fdev\u002Fnull 2>&1; then\n    echo \"Error: rtk binary not found\"\n    exit 1\nfi\n\n# Execute with explicit path (no PATH hijacking)\n\u002Fusr\u002Flocal\u002Fbin\u002Frtk \"$@\"\n```\n\n### 3. Security Testing\n\n**Command Injection Tests**:\n\n```rust\n#[cfg(test)]\nmod security_tests {\n    use super::*;\n\n    #[test]\n    fn test_command_injection_defense() {\n        \u002F\u002F Malicious input: attempt shell injection\n        let malicious_inputs = vec![\n            \"; rm -rf \u002F\",\n            \"| cat \u002Fetc\u002Fpasswd\",\n            \"$(whoami)\",\n            \"`id`\",\n            \"&& curl evil.com\",\n        ];\n\n        for input in malicious_inputs {\n            \u002F\u002F Should NOT execute injected commands\n            let result = execute_command(\"git\", &[\"log\", input]);\n\n            \u002F\u002F Either:\n            \u002F\u002F 1. Returns error (command fails safely), OR\n            \u002F\u002F 2. Treats input as literal string (no shell interpretation)\n            \u002F\u002F Both acceptable - just don't execute injection!\n        }\n    }\n\n    #[test]\n    fn test_shell_escaping() {\n        \u002F\u002F Special characters that need escaping\n        let special_chars = vec![\n            \";\", \"|\", \"&\", \"$\", \"`\", \"\\\\\", \"\\\"\", \"'\", \"\\n\", \"\\r\",\n        ];\n\n        for char in special_chars {\n            let arg = format!(\"test{}value\", char);\n            let escaped = escape_for_shell(&arg);\n\n            \u002F\u002F Escaped version should NOT be interpreted by shell\n            assert!(!escaped.contains(char) || escaped.contains('\\\\'));\n        }\n    }\n}\n```\n\n**Malicious Output Tests**:\n\n```rust\n#[test]\nfn test_malicious_output_handling() {\n    \u002F\u002F Malformed outputs that could crash RTK\n    let malicious_outputs = vec![\n        \"\", \u002F\u002F Empty\n        \"\\n\\n\\n\", \u002F\u002F Only newlines\n        \"x\".repeat(1_000_000), \u002F\u002F 1MB of 'x' (memory exhaustion)\n        \"\\x00\\x01\\x02\", \u002F\u002F Binary data\n        \"\\u{FFFD}\".repeat(1000), \u002F\u002F Unicode replacement chars\n    ];\n\n    for output in malicious_outputs {\n        let result = filter_git_log(&output);\n\n        \u002F\u002F Should either:\n        \u002F\u002F 1. Return Ok with filtered output, OR\n        \u002F\u002F 2. Return Err (graceful failure)\n        \u002F\u002F Both acceptable - just don't panic!\n        assert!(result.is_ok() || result.is_err());\n    }\n}\n```\n\n## Security Vulnerabilities Checklist\n\n### Command Injection (? Critical)\n\n- [ ] **No shell=true**: Never use `.arg(\"-c\")` with user input\n- [ ] **Command builder**: Use `std::process::Command` API (not shell strings)\n- [ ] **Input validation**: Validate\u002Fsanitize before command execution\n- [ ] **Whitelist approach**: Only allow known-safe commands\n\n**Detection**:\n```bash\n# Find dangerous shell execution\nrg \"\\.arg\\(\\\"-c\\\"\\)\" --type rust src\u002F\nrg \"std::process::Command::new\\(\\\"sh\\\"\\)\" --type rust src\u002F\nrg \"format!.*\\{.*Command\" --type rust src\u002F\n```\n\n### Shell Escaping (? Critical)\n\n- [ ] **Platform-specific**: Test escaping on macOS, Linux, Windows\n- [ ] **Special chars**: Handle `;`, `|`, `&`, `$`, `` ` ``, `\\`, `\"`, `'`, `\\n`\n- [ ] **Use shell-escape crate**: Don't roll your own escaping\n- [ ] **Cross-platform tests**: `#[cfg(target_os = \"...\")]` tests\n\n**Detection**:\n```bash\n# Find potential escaping issues\nrg \"format!.*\\{.*args\" --type rust src\u002F\nrg \"\\.join\\(\\\" \\\"\\)\" --type rust src\u002F\n```\n\n### Hook Security (? High)\n\n- [ ] **Permission checks**: Verify hooks are executable (`-rwxr-xr-x`)\n- [ ] **Source validation**: Only execute hooks from `.claude\u002Fhooks\u002F`\n- [ ] **Environment validation**: Check `$CLAUDE_CODE_HOOK_BASH_TEMPLATE`\n- [ ] **No dynamic evaluation**: No `eval` or `source` of untrusted files\n\n**Hook security checklist**:\n```bash\n#!\u002Fbin\u002Fbash\n# rtk-rewrite.sh\n\n# 1. Verify Claude Code context\nif [ -z \"$CLAUDE_CODE_HOOK_BASH_TEMPLATE\" ]; then\n    exit 1\nfi\n\n# 2. Verify RTK binary exists\nif ! command -v rtk >\u002Fdev\u002Fnull 2>&1; then\n    exit 1\nfi\n\n# 3. Use absolute path (prevent PATH hijacking)\nRTK_BIN=$(which rtk)\n\n# 4. Validate RTK version (prevent downgrade attacks)\nif ! \"$RTK_BIN\" --version | grep -q \"rtk 0.16\"; then\n    echo \"Warning: RTK version mismatch\"\nfi\n\n# 5. Execute with explicit path\n\"$RTK_BIN\" \"$@\"\n```\n\n### Malicious Output (? Medium)\n\n- [ ] **No .unwrap()**: Use `Result` for parsing, graceful error handling\n- [ ] **Bounds checking**: Verify string lengths before slicing\n- [ ] **Regex timeouts**: Prevent ReDoS (Regular Expression Denial of Service)\n- [ ] **Memory limits**: Cap output size before parsing\n\n**Parsing safety pattern**:\n```rust\nfn safe_parse(output: &str) -> Result\u003CString> {\n    \u002F\u002F 1. Check output size (prevent memory exhaustion)\n    if output.len() > 10_000_000 {\n        bail!(\"Output too large (>10MB)\");\n    }\n\n    \u002F\u002F 2. Validate format (prevent malformed input)\n    if !output.starts_with(\"commit \") {\n        bail!(\"Invalid git log format\");\n    }\n\n    \u002F\u002F 3. Bounds checking (prevent panics)\n    let first_line = output.lines().next()\n        .ok_or_else(|| anyhow::anyhow!(\"Empty output\"))?;\n\n    if first_line.len() \u003C 47 {\n        bail!(\"Commit hash too short\");\n    }\n\n    \u002F\u002F 4. Safe extraction\n    Ok(first_line[7..47].to_string())\n}\n```\n\n## Security Best Practices\n\n### Input Validation\n\n**Whitelist approach** (safer than blacklist):\n\n```rust\nfn validate_command(cmd: &str) -> Result\u003C()> {\n    \u002F\u002F ✅ SAFE: Whitelist known-safe commands\n    const ALLOWED_COMMANDS: &[&str] = &[\n        \"git\", \"cargo\", \"gh\", \"pnpm\", \"docker\",\n        \"rustc\", \"clippy\", \"rustfmt\",\n    ];\n\n    if !ALLOWED_COMMANDS.contains(&cmd) {\n        bail!(\"Command '{}' not allowed\", cmd);\n    }\n\n    Ok(())\n}\n\n\u002F\u002F ❌ UNSAFE: Blacklist approach (easy to bypass)\nfn validate_command_unsafe(cmd: &str) -> Result\u003C()> {\n    const BLOCKED: &[&str] = &[\"rm\", \"dd\", \"mkfs\"];\n\n    if BLOCKED.contains(&cmd) {\n        bail!(\"Command '{}' blocked\", cmd);\n    }\n\n    Ok(())\n    \u002F\u002F Attacker can use: \u002Fbin\u002Frm, rm.exe, RM (case variation), etc.\n}\n```\n\n### Shell Escaping\n\n**Use dedicated library**:\n\n```rust\nuse shell_escape::escape;\n\nfn escape_arg(arg: &str) -> String {\n    \u002F\u002F ✅ SAFE: Use battle-tested escaping library\n    escape(arg.into()).into()\n}\n\n\u002F\u002F ❌ UNSAFE: Roll your own escaping (likely has bugs)\nfn escape_arg_unsafe(arg: &str) -> String {\n    arg.replace('\"', r#\"\\\"\"#) \u002F\u002F Misses many special chars!\n}\n```\n\n**Platform-specific escaping**:\n\n```rust\n#[cfg(target_os = \"windows\")]\nfn escape_for_shell(arg: &str) -> String {\n    \u002F\u002F PowerShell escaping\n    format!(\"\\\"{}\\\"\", arg.replace('\"', \"`\\\"\"))\n}\n\n#[cfg(not(target_os = \"windows\"))]\nfn escape_for_shell(arg: &str) -> String {\n    \u002F\u002F Bash\u002Fzsh escaping\n    shell_escape::escape(arg.into()).into()\n}\n```\n\n### Secure Command Execution\n\n**Always use Command builder**:\n\n```rust\nuse std::process::Command;\n\n\u002F\u002F ✅ SAFE: Command builder (no shell)\nfn execute_git(args: &[&str]) -> Result\u003COutput> {\n    Command::new(\"git\")\n        .args(args) \u002F\u002F Safely escaped\n        .output()\n        .context(\"Failed to execute git\")\n}\n\n\u002F\u002F ❌ UNSAFE: Shell string concatenation\nfn execute_git_unsafe(args: &[&str]) -> Result\u003COutput> {\n    let cmd = format!(\"git {}\", args.join(\" \"));\n    Command::new(\"sh\")\n        .arg(\"-c\")\n        .arg(&cmd) \u002F\u002F Shell interprets args!\n        .output()\n}\n```\n\n## Security Audit Command Reference\n\n**Find potential vulnerabilities**:\n\n```bash\n# Command injection\nrg \"\\.arg\\(\\\"-c\\\"\\)\" --type rust src\u002F\nrg \"format!.*Command\" --type rust src\u002F\n\n# Shell escaping\nrg \"\\.join\\(\\\" \\\"\\)\" --type rust src\u002F\nrg \"format!.*\\{.*args\" --type rust src\u002F\n\n# Unsafe unwraps (can panic on malicious input)\nrg \"\\.unwrap\\(\\)\" --type rust src\u002F\n\n# Bounds violations\nrg \"\\[.*\\.\\.\\.\\]\" --type rust src\u002F\nrg \"\\[.*\\.\\.]\" --type rust src\u002F\n\n# Hook security\nrg \"eval|source\" --type bash .claude\u002Fhooks\u002F\n```\n\n## Incident Response\n\n**If vulnerability discovered**:\n\n1. **Assess severity**: Use CVSS scoring (Critical\u002FHigh\u002FMedium\u002FLow)\n2. **Develop patch**: Fix vulnerability in isolated branch\n3. **Test fix**: Verify with security tests + integration tests\n4. **Release hotfix**: PATCH version bump (e.g., v0.16.0 → v0.16.1)\n5. **Disclose responsibly**: GitHub Security Advisory, CVE if applicable\n\n**Example advisory template**:\n\n```markdown\n## Security Advisory: Command Injection in rtk v0.16.0\n\n**Severity**: CRITICAL (CVSS 9.8)\n**Affected versions**: v0.15.0 - v0.16.0\n**Fixed in**: v0.16.1\n\n**Description**:\nRTK versions 0.15.0 through 0.16.0 are vulnerable to command injection\nvia malicious git repository names. An attacker can execute arbitrary\nshell commands by creating a repository with special characters in the name.\n\n**Impact**:\nRemote code execution with user privileges.\n\n**Mitigation**:\nUpgrade to v0.16.1 immediately. As a workaround, avoid using RTK in\ndirectories with untrusted repository names.\n\n**Credits**:\nReported by: Security Researcher Name\n```\n\n## Security Resources\n\n**Tools**:\n- `cargo audit` - Dependency vulnerability scanning\n- `cargo-geiger` - Unsafe code detection\n- `cargo-deny` - Dependency policy enforcement\n- `semgrep` - Static analysis for security patterns\n\n**Run security checks**:\n```bash\n# Dependency vulnerabilities\ncargo install cargo-audit\ncargo audit\n\n# Unsafe code detection\ncargo install cargo-geiger\ncargo geiger\n\n# Static analysis\ncargo install semgrep\nsemgrep --config auto\n```\n",{"description":7,"allowed-tools":34},"Read Grep Glob Bash","main","6079b2fd99329bb8a099e7708b0567b93edf8206","third_party\u002Frtk\u002F.claude\u002Fskills\u002Fsecurity-guardian",2,"https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus","https:\u002F\u002Fgithub.com\u002FMDMAtk","2026-11-07 14:28:49",{"source":8,"skillName":5,"commands":43,"prompt":53,"zipAvailable":54},[44,47,50],{"tool":45,"command":46},"npx","npx skills add MDMAtk\u002FTormentNexus --skill security-guardian",{"tool":48,"command":49},"bunx","bunx skills add MDMAtk\u002FTormentNexus --skill security-guardian",{"tool":51,"command":52},"pnpm","pnpm dlx skills add MDMAtk\u002FTormentNexus --skill security-guardian","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Fsecurity-guardian\u002FSKILL.md",true,[],[57,87,100,117,134,147],{"skillNo":58,"name":59,"nameZh":6,"description":60,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":61,"categories":62,"keywords":68,"publishedAt":29},"01M6C5659TDGC363E5EWMQZJGZ","tdd-rust","TDD workflow for RTK filter development. Red-Green-Refactor with Rust idioms. Real fixtures, token savings assertions, snapshot tests with insta. Auto-triggers on new filter implementation.","https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Ftdd-rust\u002FSKILL.md",[63],{"categoryNo":64,"categoryName":65,"taxonomyType":66,"taxonomyTypeDesc":67},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[69,70,71,72,73,74,75,76,77,78,79,80,26,24,81,82,83,84,85,86],"tdd rust","tdd","rust","coverage","token-savings","snapshots","filters","red-green-refactor","test coverage","auto","fix","testing","development","add command","write tests","test","workflow","implement",{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":29},"01M6C562NZPX3J6W8F6P4KZMV1","ship","Build, commit, push & version bump workflow - automates the complete release cycle","https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Fship\u002FSKILL.md",[],[89,94,95,96,97,27,25,24,98,99,85],"push","version","edit","write","release","commit",{"skillNo":101,"name":102,"nameZh":6,"description":103,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":104,"categories":105,"keywords":106,"publishedAt":29},"01M6C55XHCBK612DXDAD08GGE1","rtk-triage","Triage complet RTK : exécute issue-triage + pr-triage en parallèle, puis croise les données pour détecter doubles couvertures, trous sécurité, P0 sans PR, et conflits internes. Sauvegarde dans claudedocs\u002FRTK-YYYY-MM-DD.md. Args: \"en\"\u002F\"fr\" pour la langue (défaut: fr), \"save\" pour forcer la sauvegarde.","https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Frtk-triage\u002FSKILL.md",[],[107,108,28,24,109,97,27,110,111,112,113,114,115,18,116],"triage","rtk triage","cross-analysis","analysis","sans","dd","issues","pr","orchestration","save",{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":127,"publishedAt":29},"01M6C55V1RDCNFJ019HRA16ZHM","rtk-tdd","Enforces TDD (Red-Green-Refactor) for Rust development. Auto-triggers on implementation, testing, refactoring, and bug fixing tasks. Provides Rust-idiomatic testing patterns with anyhow\u002Fthiserror, cfg(test), and Arrange-Act-Assert workflow.","https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Frtk-tdd\u002FSKILL.md",[123,126],{"categoryNo":124,"categoryName":125,"taxonomyType":66,"taxonomyTypeDesc":67},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":64,"categoryName":65,"taxonomyType":66,"taxonomyTypeDesc":67},[128,28,70,81,129,84,85,71,80,130,76,78,96,97,27,131,24,132,133],"rtk tdd","tasks","refactor","refactoring","CFG","testing patterns",{"skillNo":135,"name":136,"nameZh":6,"description":137,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":138,"categories":139,"keywords":140,"publishedAt":29},"01M6C55RQ5QH5MBSYXMC8KF0WV","repo-recap","Generate a comprehensive repo recap (PRs, issues, releases) for sharing with team. Pass \"en\" or \"fr\" as argument for language (default fr).","https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Frepo-recap\u002FSKILL.md",[],[141,142,143,27,25,24,144,145,113,146],"repo recap","repo","recap","team","sharing","generate",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":153,"publishedAt":29},"01M6C55PB2B52ADBYHV6H0NMHD","pr-triage","PR triage: audit open PRs, deep review selected ones, draft and post review comments. Args: \"all\" to review all, PR numbers to focus (e.g. \"42 57\"), \"en\"\u002F\"fr\" for language, no arg = audit only in French.","https:\u002F\u002Fgithub.com\u002FMDMAtk\u002FTormentNexus\u002Fblob\u002Fmain\u002Fthird_party\u002Frtk\u002F.claude\u002Fskills\u002Fpr-triage\u002FSKILL.md",[],[114,107,154,155,156,157,158,25,159,24,27,28,160,161,162],"pr triage","code","code-review","deep review","github","open","only","review","audit"]