@hasna/terminal 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/cli.js +29 -12
  2. package/package.json +1 -1
  3. package/src/ai.ts +50 -36
  4. package/src/cli.tsx +29 -12
  5. package/src/context-hints.ts +89 -0
  6. package/src/discover.ts +238 -0
  7. package/src/economy.ts +53 -0
  8. package/src/output-store.ts +65 -0
  9. package/src/providers/index.ts +4 -4
  10. package/src/sessions-db.ts +81 -0
  11. package/temp/rtk/.claude/agents/code-reviewer.md +221 -0
  12. package/temp/rtk/.claude/agents/debugger.md +519 -0
  13. package/temp/rtk/.claude/agents/rtk-testing-specialist.md +461 -0
  14. package/temp/rtk/.claude/agents/rust-rtk.md +511 -0
  15. package/temp/rtk/.claude/agents/technical-writer.md +355 -0
  16. package/temp/rtk/.claude/commands/diagnose.md +352 -0
  17. package/temp/rtk/.claude/commands/test-routing.md +362 -0
  18. package/temp/rtk/.claude/hooks/bash/pre-commit-format.sh +16 -0
  19. package/temp/rtk/.claude/hooks/rtk-rewrite.sh +70 -0
  20. package/temp/rtk/.claude/hooks/rtk-suggest.sh +152 -0
  21. package/temp/rtk/.claude/rules/cli-testing.md +526 -0
  22. package/temp/rtk/.claude/skills/issue-triage/SKILL.md +348 -0
  23. package/temp/rtk/.claude/skills/issue-triage/templates/issue-comment.md +134 -0
  24. package/temp/rtk/.claude/skills/performance.md +435 -0
  25. package/temp/rtk/.claude/skills/pr-triage/SKILL.md +315 -0
  26. package/temp/rtk/.claude/skills/pr-triage/templates/review-comment.md +71 -0
  27. package/temp/rtk/.claude/skills/repo-recap.md +206 -0
  28. package/temp/rtk/.claude/skills/rtk-tdd/SKILL.md +78 -0
  29. package/temp/rtk/.claude/skills/rtk-tdd/references/testing-patterns.md +124 -0
  30. package/temp/rtk/.claude/skills/security-guardian.md +503 -0
  31. package/temp/rtk/.claude/skills/ship.md +404 -0
  32. package/temp/rtk/.github/workflows/benchmark.yml +34 -0
  33. package/temp/rtk/.github/workflows/dco-check.yaml +12 -0
  34. package/temp/rtk/.github/workflows/release-please.yml +51 -0
  35. package/temp/rtk/.github/workflows/release.yml +343 -0
  36. package/temp/rtk/.github/workflows/security-check.yml +135 -0
  37. package/temp/rtk/.github/workflows/validate-docs.yml +78 -0
  38. package/temp/rtk/.release-please-manifest.json +3 -0
  39. package/temp/rtk/ARCHITECTURE.md +1491 -0
  40. package/temp/rtk/CHANGELOG.md +640 -0
  41. package/temp/rtk/CLAUDE.md +605 -0
  42. package/temp/rtk/CONTRIBUTING.md +199 -0
  43. package/temp/rtk/Cargo.lock +1668 -0
  44. package/temp/rtk/Cargo.toml +64 -0
  45. package/temp/rtk/Formula/rtk.rb +43 -0
  46. package/temp/rtk/INSTALL.md +390 -0
  47. package/temp/rtk/LICENSE +21 -0
  48. package/temp/rtk/README.md +386 -0
  49. package/temp/rtk/README_es.md +159 -0
  50. package/temp/rtk/README_fr.md +197 -0
  51. package/temp/rtk/README_ja.md +159 -0
  52. package/temp/rtk/README_ko.md +159 -0
  53. package/temp/rtk/README_zh.md +167 -0
  54. package/temp/rtk/ROADMAP.md +15 -0
  55. package/temp/rtk/SECURITY.md +217 -0
  56. package/temp/rtk/TEST_EXEC_TIME.md +102 -0
  57. package/temp/rtk/build.rs +57 -0
  58. package/temp/rtk/docs/AUDIT_GUIDE.md +432 -0
  59. package/temp/rtk/docs/FEATURES.md +1410 -0
  60. package/temp/rtk/docs/TROUBLESHOOTING.md +309 -0
  61. package/temp/rtk/docs/filter-workflow.md +102 -0
  62. package/temp/rtk/docs/images/gain-dashboard.jpg +0 -0
  63. package/temp/rtk/docs/tracking.md +583 -0
  64. package/temp/rtk/hooks/opencode-rtk.ts +39 -0
  65. package/temp/rtk/hooks/rtk-awareness.md +29 -0
  66. package/temp/rtk/hooks/rtk-rewrite.sh +61 -0
  67. package/temp/rtk/hooks/test-rtk-rewrite.sh +442 -0
  68. package/temp/rtk/install.sh +124 -0
  69. package/temp/rtk/release-please-config.json +10 -0
  70. package/temp/rtk/scripts/benchmark.sh +592 -0
  71. package/temp/rtk/scripts/check-installation.sh +162 -0
  72. package/temp/rtk/scripts/install-local.sh +37 -0
  73. package/temp/rtk/scripts/rtk-economics.sh +137 -0
  74. package/temp/rtk/scripts/test-all.sh +561 -0
  75. package/temp/rtk/scripts/test-aristote.sh +227 -0
  76. package/temp/rtk/scripts/test-tracking.sh +79 -0
  77. package/temp/rtk/scripts/update-readme-metrics.sh +32 -0
  78. package/temp/rtk/scripts/validate-docs.sh +73 -0
  79. package/temp/rtk/src/aws_cmd.rs +880 -0
  80. package/temp/rtk/src/binlog.rs +1645 -0
  81. package/temp/rtk/src/cargo_cmd.rs +1727 -0
  82. package/temp/rtk/src/cc_economics.rs +1157 -0
  83. package/temp/rtk/src/ccusage.rs +340 -0
  84. package/temp/rtk/src/config.rs +187 -0
  85. package/temp/rtk/src/container.rs +855 -0
  86. package/temp/rtk/src/curl_cmd.rs +134 -0
  87. package/temp/rtk/src/deps.rs +268 -0
  88. package/temp/rtk/src/diff_cmd.rs +367 -0
  89. package/temp/rtk/src/discover/mod.rs +274 -0
  90. package/temp/rtk/src/discover/provider.rs +388 -0
  91. package/temp/rtk/src/discover/registry.rs +2022 -0
  92. package/temp/rtk/src/discover/report.rs +202 -0
  93. package/temp/rtk/src/discover/rules.rs +667 -0
  94. package/temp/rtk/src/display_helpers.rs +402 -0
  95. package/temp/rtk/src/dotnet_cmd.rs +1771 -0
  96. package/temp/rtk/src/dotnet_format_report.rs +133 -0
  97. package/temp/rtk/src/dotnet_trx.rs +593 -0
  98. package/temp/rtk/src/env_cmd.rs +204 -0
  99. package/temp/rtk/src/filter.rs +462 -0
  100. package/temp/rtk/src/filters/README.md +52 -0
  101. package/temp/rtk/src/filters/ansible-playbook.toml +34 -0
  102. package/temp/rtk/src/filters/basedpyright.toml +47 -0
  103. package/temp/rtk/src/filters/biome.toml +45 -0
  104. package/temp/rtk/src/filters/brew-install.toml +37 -0
  105. package/temp/rtk/src/filters/composer-install.toml +40 -0
  106. package/temp/rtk/src/filters/df.toml +16 -0
  107. package/temp/rtk/src/filters/dotnet-build.toml +64 -0
  108. package/temp/rtk/src/filters/du.toml +16 -0
  109. package/temp/rtk/src/filters/fail2ban-client.toml +15 -0
  110. package/temp/rtk/src/filters/gcc.toml +49 -0
  111. package/temp/rtk/src/filters/gcloud.toml +22 -0
  112. package/temp/rtk/src/filters/hadolint.toml +24 -0
  113. package/temp/rtk/src/filters/helm.toml +29 -0
  114. package/temp/rtk/src/filters/iptables.toml +27 -0
  115. package/temp/rtk/src/filters/jj.toml +28 -0
  116. package/temp/rtk/src/filters/jq.toml +24 -0
  117. package/temp/rtk/src/filters/make.toml +41 -0
  118. package/temp/rtk/src/filters/markdownlint.toml +24 -0
  119. package/temp/rtk/src/filters/mix-compile.toml +27 -0
  120. package/temp/rtk/src/filters/mix-format.toml +15 -0
  121. package/temp/rtk/src/filters/mvn-build.toml +44 -0
  122. package/temp/rtk/src/filters/oxlint.toml +43 -0
  123. package/temp/rtk/src/filters/ping.toml +63 -0
  124. package/temp/rtk/src/filters/pio-run.toml +40 -0
  125. package/temp/rtk/src/filters/poetry-install.toml +50 -0
  126. package/temp/rtk/src/filters/pre-commit.toml +35 -0
  127. package/temp/rtk/src/filters/ps.toml +16 -0
  128. package/temp/rtk/src/filters/quarto-render.toml +41 -0
  129. package/temp/rtk/src/filters/rsync.toml +48 -0
  130. package/temp/rtk/src/filters/shellcheck.toml +27 -0
  131. package/temp/rtk/src/filters/shopify-theme.toml +29 -0
  132. package/temp/rtk/src/filters/skopeo.toml +45 -0
  133. package/temp/rtk/src/filters/sops.toml +16 -0
  134. package/temp/rtk/src/filters/ssh.toml +44 -0
  135. package/temp/rtk/src/filters/stat.toml +34 -0
  136. package/temp/rtk/src/filters/swift-build.toml +41 -0
  137. package/temp/rtk/src/filters/systemctl-status.toml +33 -0
  138. package/temp/rtk/src/filters/terraform-plan.toml +35 -0
  139. package/temp/rtk/src/filters/tofu-fmt.toml +16 -0
  140. package/temp/rtk/src/filters/tofu-init.toml +38 -0
  141. package/temp/rtk/src/filters/tofu-plan.toml +35 -0
  142. package/temp/rtk/src/filters/tofu-validate.toml +17 -0
  143. package/temp/rtk/src/filters/trunk-build.toml +39 -0
  144. package/temp/rtk/src/filters/ty.toml +50 -0
  145. package/temp/rtk/src/filters/uv-sync.toml +37 -0
  146. package/temp/rtk/src/filters/xcodebuild.toml +99 -0
  147. package/temp/rtk/src/filters/yamllint.toml +25 -0
  148. package/temp/rtk/src/find_cmd.rs +598 -0
  149. package/temp/rtk/src/format_cmd.rs +386 -0
  150. package/temp/rtk/src/gain.rs +723 -0
  151. package/temp/rtk/src/gh_cmd.rs +1651 -0
  152. package/temp/rtk/src/git.rs +2012 -0
  153. package/temp/rtk/src/go_cmd.rs +592 -0
  154. package/temp/rtk/src/golangci_cmd.rs +254 -0
  155. package/temp/rtk/src/grep_cmd.rs +288 -0
  156. package/temp/rtk/src/gt_cmd.rs +810 -0
  157. package/temp/rtk/src/hook_audit_cmd.rs +283 -0
  158. package/temp/rtk/src/hook_check.rs +171 -0
  159. package/temp/rtk/src/init.rs +1859 -0
  160. package/temp/rtk/src/integrity.rs +537 -0
  161. package/temp/rtk/src/json_cmd.rs +231 -0
  162. package/temp/rtk/src/learn/detector.rs +628 -0
  163. package/temp/rtk/src/learn/mod.rs +119 -0
  164. package/temp/rtk/src/learn/report.rs +184 -0
  165. package/temp/rtk/src/lint_cmd.rs +694 -0
  166. package/temp/rtk/src/local_llm.rs +316 -0
  167. package/temp/rtk/src/log_cmd.rs +248 -0
  168. package/temp/rtk/src/ls.rs +324 -0
  169. package/temp/rtk/src/main.rs +2482 -0
  170. package/temp/rtk/src/mypy_cmd.rs +389 -0
  171. package/temp/rtk/src/next_cmd.rs +241 -0
  172. package/temp/rtk/src/npm_cmd.rs +236 -0
  173. package/temp/rtk/src/parser/README.md +267 -0
  174. package/temp/rtk/src/parser/error.rs +46 -0
  175. package/temp/rtk/src/parser/formatter.rs +336 -0
  176. package/temp/rtk/src/parser/mod.rs +311 -0
  177. package/temp/rtk/src/parser/types.rs +119 -0
  178. package/temp/rtk/src/pip_cmd.rs +302 -0
  179. package/temp/rtk/src/playwright_cmd.rs +479 -0
  180. package/temp/rtk/src/pnpm_cmd.rs +573 -0
  181. package/temp/rtk/src/prettier_cmd.rs +221 -0
  182. package/temp/rtk/src/prisma_cmd.rs +482 -0
  183. package/temp/rtk/src/psql_cmd.rs +382 -0
  184. package/temp/rtk/src/pytest_cmd.rs +384 -0
  185. package/temp/rtk/src/read.rs +217 -0
  186. package/temp/rtk/src/rewrite_cmd.rs +50 -0
  187. package/temp/rtk/src/ruff_cmd.rs +402 -0
  188. package/temp/rtk/src/runner.rs +271 -0
  189. package/temp/rtk/src/summary.rs +297 -0
  190. package/temp/rtk/src/tee.rs +405 -0
  191. package/temp/rtk/src/telemetry.rs +248 -0
  192. package/temp/rtk/src/toml_filter.rs +1655 -0
  193. package/temp/rtk/src/tracking.rs +1416 -0
  194. package/temp/rtk/src/tree.rs +209 -0
  195. package/temp/rtk/src/tsc_cmd.rs +259 -0
  196. package/temp/rtk/src/utils.rs +432 -0
  197. package/temp/rtk/src/verify_cmd.rs +47 -0
  198. package/temp/rtk/src/vitest_cmd.rs +385 -0
  199. package/temp/rtk/src/wc_cmd.rs +401 -0
  200. package/temp/rtk/src/wget_cmd.rs +260 -0
  201. package/temp/rtk/tests/fixtures/dotnet/build_failed.txt +11 -0
  202. package/temp/rtk/tests/fixtures/dotnet/format_changes.json +31 -0
  203. package/temp/rtk/tests/fixtures/dotnet/format_empty.json +1 -0
  204. package/temp/rtk/tests/fixtures/dotnet/format_success.json +12 -0
  205. package/temp/rtk/tests/fixtures/dotnet/test_failed.txt +18 -0
@@ -0,0 +1,124 @@
1
+ # RTK Testing Patterns Reference
2
+
3
+ ## Untested Modules Backlog
4
+
5
+ Prioritized by testability (pure functions first, I/O-heavy last).
6
+
7
+ ### High Priority (pure functions, trivial to test)
8
+
9
+ | Module | Testable Functions | Notes |
10
+ |--------|-------------------|-------|
11
+ | `diff_cmd.rs` | `compute_diff`, `similarity`, `truncate`, `condense_unified_diff` | 4 pure functions, 0 tests |
12
+ | `env_cmd.rs` | `mask_value`, `is_lang_var`, `is_cloud_var`, `is_tool_var`, `is_interesting_var` | 5 categorization functions |
13
+
14
+ ### Medium Priority (need tempfile or parsed input)
15
+
16
+ | Module | Testable Functions | Notes |
17
+ |--------|-------------------|-------|
18
+ | `tracking.rs` | `estimate_tokens`, `Tracker::new`, query methods | Use tempfile for SQLite |
19
+ | `config.rs` | `Config::default`, config parsing | Test default values and TOML parsing |
20
+ | `deps.rs` | Dependency file parsing | Test with sample Cargo.toml/package.json strings |
21
+ | `summary.rs` | Output type detection heuristics | Pure string analysis |
22
+
23
+ ### Low Priority (heavy I/O, CLI wiring)
24
+
25
+ | Module | Testable Functions | Notes |
26
+ |--------|-------------------|-------|
27
+ | `container.rs` | Docker/kubectl output filters | Requires mocking Command output |
28
+ | `find_cmd.rs` | Directory grouping logic | Filesystem-dependent |
29
+ | `wget_cmd.rs` | `compact_url`, `format_size`, `truncate_line`, `extract_filename_from_output` | Some pure helpers worth testing |
30
+ | `gain.rs` | Display formatting | Depends on tracking DB |
31
+ | `init.rs` | CLAUDE.md generation | File I/O |
32
+ | `main.rs` | CLI routing | Covered by smoke tests |
33
+
34
+ ## RTK Test Patterns
35
+
36
+ ### Pattern 1: Filter Function (most common in RTK)
37
+
38
+ ```rust
39
+ #[test]
40
+ fn test_FILTER_happy_path() {
41
+ // Arrange: raw command output as string literal
42
+ let input = r#"
43
+ line of noise
44
+ line with relevant data
45
+ more noise
46
+ "#;
47
+ // Act
48
+ let result = filter_COMMAND(input);
49
+ // Assert: output contains expected, excludes noise
50
+ assert!(result.contains("relevant data"));
51
+ assert!(!result.contains("noise"));
52
+ }
53
+ ```
54
+
55
+ Used in: `git.rs`, `grep_cmd.rs`, `lint_cmd.rs`, `tsc_cmd.rs`, `vitest_cmd.rs`, `pnpm_cmd.rs`, `next_cmd.rs`, `prettier_cmd.rs`, `playwright_cmd.rs`, `prisma_cmd.rs`
56
+
57
+ ### Pattern 2: Pure Computation
58
+
59
+ ```rust
60
+ #[test]
61
+ fn test_FUNCTION_deterministic() {
62
+ assert_eq!(truncate("hello world", 8), "hello...");
63
+ assert_eq!(truncate("short", 10), "short");
64
+ }
65
+ ```
66
+
67
+ Used in: `gh_cmd.rs` (`truncate`), `utils.rs` (`truncate`, `format_tokens`, `format_usd`)
68
+
69
+ ### Pattern 3: Validation / Security
70
+
71
+ ```rust
72
+ #[test]
73
+ fn test_VALIDATOR_rejects_injection() {
74
+ assert!(!is_valid("malicious; rm -rf /"));
75
+ assert!(!is_valid("../../../etc/passwd"));
76
+ }
77
+ ```
78
+
79
+ Used in: `pnpm_cmd.rs` (`is_valid_package_name`)
80
+
81
+ ### Pattern 4: ANSI Stripping
82
+
83
+ ```rust
84
+ #[test]
85
+ fn test_strip_ansi() {
86
+ let input = "\x1b[32mgreen\x1b[0m normal";
87
+ let output = strip_ansi(input);
88
+ assert_eq!(output, "green normal");
89
+ assert!(!output.contains("\x1b["));
90
+ }
91
+ ```
92
+
93
+ Used in: `vitest_cmd.rs`, `utils.rs`
94
+
95
+ ## Test Skeleton Template
96
+
97
+ ```rust
98
+ #[cfg(test)]
99
+ mod tests {
100
+ use super::*;
101
+
102
+ #[test]
103
+ fn test_FUNCTION_happy_path() {
104
+ // Arrange
105
+ let input = r#"..."#;
106
+ // Act
107
+ let result = FUNCTION(input);
108
+ // Assert
109
+ assert!(result.contains("expected"));
110
+ assert!(!result.contains("noise"));
111
+ }
112
+
113
+ #[test]
114
+ fn test_FUNCTION_empty_input() {
115
+ let result = FUNCTION("");
116
+ assert!(...);
117
+ }
118
+
119
+ #[test]
120
+ fn test_FUNCTION_edge_case() {
121
+ // Boundary conditions: very long input, special chars, unicode
122
+ }
123
+ }
124
+ ```
@@ -0,0 +1,503 @@
1
+ ---
2
+ description: CLI security expert for RTK - command injection, shell escaping, hook security
3
+ ---
4
+
5
+ # Security Guardian
6
+
7
+ Comprehensive security analysis for RTK CLI tool, focusing on **command injection**, **shell escaping**, **hook security**, and **malicious input handling**.
8
+
9
+ ## When to Use
10
+
11
+ - **Automatically triggered**: After filter changes, shell command execution logic, hook modifications
12
+ - **Manual invocation**: Before release, after security-sensitive code changes
13
+ - **Proactive**: When handling user input, executing shell commands, or parsing untrusted output
14
+
15
+ ## RTK Security Threat Model
16
+
17
+ RTK faces unique security challenges as a CLI proxy that:
18
+ 1. **Executes shell commands** based on user input
19
+ 2. **Parses untrusted command output** (git, cargo, gh, etc.)
20
+ 3. **Integrates with Claude Code hooks** (rtk-rewrite.sh, rtk-suggest.sh)
21
+ 4. **Routes commands transparently** (command injection vectors)
22
+
23
+ ### Threat Categories
24
+
25
+ | Threat | Severity | Impact | Mitigation |
26
+ |--------|----------|--------|------------|
27
+ | **Command Injection** | 🔴 CRITICAL | Remote code execution | Input validation, shell escaping |
28
+ | **Shell Escaping** | 🔴 CRITICAL | Arbitrary command execution | Platform-specific escaping |
29
+ | **Hook Injection** | 🟡 HIGH | Hook hijacking, command interception | Permission checks, signature validation |
30
+ | **Malicious Output** | 🟡 MEDIUM | RTK crash, DoS | Robust parsing, error handling |
31
+ | **Path Traversal** | 🟢 LOW | File access outside filters/ | Path sanitization |
32
+
33
+ ## Security Analysis Workflow
34
+
35
+ ### 1. Threat Identification
36
+
37
+ **Questions to ask** for every code change:
38
+
39
+ ```
40
+ Input Validation:
41
+ - Does this code accept user input?
42
+ - Is the input validated before use?
43
+ - Can special characters (;, |, &, $, `, \, etc.) cause issues?
44
+
45
+ Shell Execution:
46
+ - Does this code execute shell commands?
47
+ - Are command arguments properly escaped?
48
+ - Is std::process::Command used (safe) or shell=true (dangerous)?
49
+
50
+ Output Parsing:
51
+ - Does this code parse external command output?
52
+ - Can malformed output cause panics or crashes?
53
+ - Are regex patterns tested against malicious input?
54
+
55
+ Hook Integration:
56
+ - Does this code modify hooks?
57
+ - Are hook permissions validated (executable bit)?
58
+ - Is hook source code integrity checked?
59
+ ```
60
+
61
+ ### 2. Code Audit Patterns
62
+
63
+ **Command Injection Detection**:
64
+
65
+ ```rust
66
+ // 🔴 CRITICAL: Shell injection vulnerability
67
+ let user_input = env::args().nth(1).unwrap();
68
+ let cmd = format!("git log {}", user_input); // DANGEROUS!
69
+ std::process::Command::new("sh")
70
+ .arg("-c")
71
+ .arg(&cmd) // Attacker can inject: `; rm -rf /`
72
+ .spawn();
73
+
74
+ // ✅ SAFE: Use Command builder, not shell
75
+ use std::process::Command;
76
+
77
+ let user_input = env::args().nth(1).unwrap();
78
+ Command::new("git")
79
+ .arg("log")
80
+ .arg(&user_input) // Safely passed as argument, not interpreted by shell
81
+ .spawn();
82
+ ```
83
+
84
+ **Shell Escaping Vulnerability**:
85
+
86
+ ```rust
87
+ // 🔴 CRITICAL: No escaping for special chars
88
+ fn execute_raw(cmd: &str, args: &[&str]) -> Result<Output> {
89
+ let full_cmd = format!("{} {}", cmd, args.join(" "));
90
+ Command::new("sh")
91
+ .arg("-c")
92
+ .arg(&full_cmd) // DANGEROUS: args not escaped
93
+ .output()
94
+ }
95
+
96
+ // ✅ SAFE: Use Command builder, automatic escaping
97
+ fn execute_raw(cmd: &str, args: &[&str]) -> Result<Output> {
98
+ Command::new(cmd)
99
+ .args(args) // Safely escaped by Command API
100
+ .output()
101
+ }
102
+ ```
103
+
104
+ **Malicious Output Handling**:
105
+
106
+ ```rust
107
+ // 🔴 CRITICAL: Panic on unexpected output
108
+ fn filter_git_log(input: &str) -> String {
109
+ let first_line = input.lines().next().unwrap(); // Panic if empty!
110
+ let hash = &first_line[7..47]; // Panic if line too short!
111
+ hash.to_string()
112
+ }
113
+
114
+ // ✅ SAFE: Graceful error handling
115
+ fn filter_git_log(input: &str) -> Result<String> {
116
+ let first_line = input.lines().next()
117
+ .ok_or_else(|| anyhow::anyhow!("Empty input"))?;
118
+
119
+ if first_line.len() < 47 {
120
+ bail!("Invalid git log format");
121
+ }
122
+
123
+ Ok(first_line[7..47].to_string())
124
+ }
125
+ ```
126
+
127
+ **Hook Injection Prevention**:
128
+
129
+ ```bash
130
+ # 🔴 CRITICAL: Hook not checking source
131
+ #!/bin/bash
132
+ # rtk-rewrite.sh
133
+
134
+ # Execute command without validation
135
+ eval "$CLAUDE_CODE_HOOK_BASH_TEMPLATE" # DANGEROUS!
136
+
137
+ # ✅ SAFE: Validate hook environment
138
+ #!/bin/bash
139
+ # rtk-rewrite.sh
140
+
141
+ # Verify running in Claude Code context
142
+ if [ -z "$CLAUDE_CODE_HOOK_BASH_TEMPLATE" ]; then
143
+ echo "Error: Not running in Claude Code context"
144
+ exit 1
145
+ fi
146
+
147
+ # Validate RTK binary exists and is executable
148
+ if ! command -v rtk >/dev/null 2>&1; then
149
+ echo "Error: rtk binary not found"
150
+ exit 1
151
+ fi
152
+
153
+ # Execute with explicit path (no PATH hijacking)
154
+ /usr/local/bin/rtk "$@"
155
+ ```
156
+
157
+ ### 3. Security Testing
158
+
159
+ **Command Injection Tests**:
160
+
161
+ ```rust
162
+ #[cfg(test)]
163
+ mod security_tests {
164
+ use super::*;
165
+
166
+ #[test]
167
+ fn test_command_injection_defense() {
168
+ // Malicious input: attempt shell injection
169
+ let malicious_inputs = vec![
170
+ "; rm -rf /",
171
+ "| cat /etc/passwd",
172
+ "$(whoami)",
173
+ "`id`",
174
+ "&& curl evil.com",
175
+ ];
176
+
177
+ for input in malicious_inputs {
178
+ // Should NOT execute injected commands
179
+ let result = execute_command("git", &["log", input]);
180
+
181
+ // Either:
182
+ // 1. Returns error (command fails safely), OR
183
+ // 2. Treats input as literal string (no shell interpretation)
184
+ // Both acceptable - just don't execute injection!
185
+ }
186
+ }
187
+
188
+ #[test]
189
+ fn test_shell_escaping() {
190
+ // Special characters that need escaping
191
+ let special_chars = vec![
192
+ ";", "|", "&", "$", "`", "\\", "\"", "'", "\n", "\r",
193
+ ];
194
+
195
+ for char in special_chars {
196
+ let arg = format!("test{}value", char);
197
+ let escaped = escape_for_shell(&arg);
198
+
199
+ // Escaped version should NOT be interpreted by shell
200
+ assert!(!escaped.contains(char) || escaped.contains('\\'));
201
+ }
202
+ }
203
+ }
204
+ ```
205
+
206
+ **Malicious Output Tests**:
207
+
208
+ ```rust
209
+ #[test]
210
+ fn test_malicious_output_handling() {
211
+ // Malformed outputs that could crash RTK
212
+ let malicious_outputs = vec![
213
+ "", // Empty
214
+ "\n\n\n", // Only newlines
215
+ "x".repeat(1_000_000), // 1MB of 'x' (memory exhaustion)
216
+ "\x00\x01\x02", // Binary data
217
+ "\u{FFFD}".repeat(1000), // Unicode replacement chars
218
+ ];
219
+
220
+ for output in malicious_outputs {
221
+ let result = filter_git_log(&output);
222
+
223
+ // Should either:
224
+ // 1. Return Ok with filtered output, OR
225
+ // 2. Return Err (graceful failure)
226
+ // Both acceptable - just don't panic!
227
+ assert!(result.is_ok() || result.is_err());
228
+ }
229
+ }
230
+ ```
231
+
232
+ ## Security Vulnerabilities Checklist
233
+
234
+ ### Command Injection (🔴 Critical)
235
+
236
+ - [ ] **No shell=true**: Never use `.arg("-c")` with user input
237
+ - [ ] **Command builder**: Use `std::process::Command` API (not shell strings)
238
+ - [ ] **Input validation**: Validate/sanitize before command execution
239
+ - [ ] **Whitelist approach**: Only allow known-safe commands
240
+
241
+ **Detection**:
242
+ ```bash
243
+ # Find dangerous shell execution
244
+ rg "\.arg\(\"-c\"\)" --type rust src/
245
+ rg "std::process::Command::new\(\"sh\"\)" --type rust src/
246
+ rg "format!.*\{.*Command" --type rust src/
247
+ ```
248
+
249
+ ### Shell Escaping (🔴 Critical)
250
+
251
+ - [ ] **Platform-specific**: Test escaping on macOS, Linux, Windows
252
+ - [ ] **Special chars**: Handle `;`, `|`, `&`, `$`, `` ` ``, `\`, `"`, `'`, `\n`
253
+ - [ ] **Use shell-escape crate**: Don't roll your own escaping
254
+ - [ ] **Cross-platform tests**: `#[cfg(target_os = "...")]` tests
255
+
256
+ **Detection**:
257
+ ```bash
258
+ # Find potential escaping issues
259
+ rg "format!.*\{.*args" --type rust src/
260
+ rg "\.join\(\" \"\)" --type rust src/
261
+ ```
262
+
263
+ ### Hook Security (🟡 High)
264
+
265
+ - [ ] **Permission checks**: Verify hooks are executable (`-rwxr-xr-x`)
266
+ - [ ] **Source validation**: Only execute hooks from `.claude/hooks/`
267
+ - [ ] **Environment validation**: Check `$CLAUDE_CODE_HOOK_BASH_TEMPLATE`
268
+ - [ ] **No dynamic evaluation**: No `eval` or `source` of untrusted files
269
+
270
+ **Hook security checklist**:
271
+ ```bash
272
+ #!/bin/bash
273
+ # rtk-rewrite.sh
274
+
275
+ # 1. Verify Claude Code context
276
+ if [ -z "$CLAUDE_CODE_HOOK_BASH_TEMPLATE" ]; then
277
+ exit 1
278
+ fi
279
+
280
+ # 2. Verify RTK binary exists
281
+ if ! command -v rtk >/dev/null 2>&1; then
282
+ exit 1
283
+ fi
284
+
285
+ # 3. Use absolute path (prevent PATH hijacking)
286
+ RTK_BIN=$(which rtk)
287
+
288
+ # 4. Validate RTK version (prevent downgrade attacks)
289
+ if ! "$RTK_BIN" --version | grep -q "rtk 0.16"; then
290
+ echo "Warning: RTK version mismatch"
291
+ fi
292
+
293
+ # 5. Execute with explicit path
294
+ "$RTK_BIN" "$@"
295
+ ```
296
+
297
+ ### Malicious Output (🟡 Medium)
298
+
299
+ - [ ] **No .unwrap()**: Use `Result` for parsing, graceful error handling
300
+ - [ ] **Bounds checking**: Verify string lengths before slicing
301
+ - [ ] **Regex timeouts**: Prevent ReDoS (Regular Expression Denial of Service)
302
+ - [ ] **Memory limits**: Cap output size before parsing
303
+
304
+ **Parsing safety pattern**:
305
+ ```rust
306
+ fn safe_parse(output: &str) -> Result<String> {
307
+ // 1. Check output size (prevent memory exhaustion)
308
+ if output.len() > 10_000_000 {
309
+ bail!("Output too large (>10MB)");
310
+ }
311
+
312
+ // 2. Validate format (prevent malformed input)
313
+ if !output.starts_with("commit ") {
314
+ bail!("Invalid git log format");
315
+ }
316
+
317
+ // 3. Bounds checking (prevent panics)
318
+ let first_line = output.lines().next()
319
+ .ok_or_else(|| anyhow::anyhow!("Empty output"))?;
320
+
321
+ if first_line.len() < 47 {
322
+ bail!("Commit hash too short");
323
+ }
324
+
325
+ // 4. Safe extraction
326
+ Ok(first_line[7..47].to_string())
327
+ }
328
+ ```
329
+
330
+ ## Security Best Practices
331
+
332
+ ### Input Validation
333
+
334
+ **Whitelist approach** (safer than blacklist):
335
+
336
+ ```rust
337
+ fn validate_command(cmd: &str) -> Result<()> {
338
+ // ✅ SAFE: Whitelist known-safe commands
339
+ const ALLOWED_COMMANDS: &[&str] = &[
340
+ "git", "cargo", "gh", "pnpm", "docker",
341
+ "rustc", "clippy", "rustfmt",
342
+ ];
343
+
344
+ if !ALLOWED_COMMANDS.contains(&cmd) {
345
+ bail!("Command '{}' not allowed", cmd);
346
+ }
347
+
348
+ Ok(())
349
+ }
350
+
351
+ // ❌ UNSAFE: Blacklist approach (easy to bypass)
352
+ fn validate_command_unsafe(cmd: &str) -> Result<()> {
353
+ const BLOCKED: &[&str] = &["rm", "dd", "mkfs"];
354
+
355
+ if BLOCKED.contains(&cmd) {
356
+ bail!("Command '{}' blocked", cmd);
357
+ }
358
+
359
+ Ok(())
360
+ // Attacker can use: /bin/rm, rm.exe, RM (case variation), etc.
361
+ }
362
+ ```
363
+
364
+ ### Shell Escaping
365
+
366
+ **Use dedicated library**:
367
+
368
+ ```rust
369
+ use shell_escape::escape;
370
+
371
+ fn escape_arg(arg: &str) -> String {
372
+ // ✅ SAFE: Use battle-tested escaping library
373
+ escape(arg.into()).into()
374
+ }
375
+
376
+ // ❌ UNSAFE: Roll your own escaping (likely has bugs)
377
+ fn escape_arg_unsafe(arg: &str) -> String {
378
+ arg.replace('"', r#"\""#) // Misses many special chars!
379
+ }
380
+ ```
381
+
382
+ **Platform-specific escaping**:
383
+
384
+ ```rust
385
+ #[cfg(target_os = "windows")]
386
+ fn escape_for_shell(arg: &str) -> String {
387
+ // PowerShell escaping
388
+ format!("\"{}\"", arg.replace('"', "`\""))
389
+ }
390
+
391
+ #[cfg(not(target_os = "windows"))]
392
+ fn escape_for_shell(arg: &str) -> String {
393
+ // Bash/zsh escaping
394
+ shell_escape::escape(arg.into()).into()
395
+ }
396
+ ```
397
+
398
+ ### Secure Command Execution
399
+
400
+ **Always use Command builder**:
401
+
402
+ ```rust
403
+ use std::process::Command;
404
+
405
+ // ✅ SAFE: Command builder (no shell)
406
+ fn execute_git(args: &[&str]) -> Result<Output> {
407
+ Command::new("git")
408
+ .args(args) // Safely escaped
409
+ .output()
410
+ .context("Failed to execute git")
411
+ }
412
+
413
+ // ❌ UNSAFE: Shell string concatenation
414
+ fn execute_git_unsafe(args: &[&str]) -> Result<Output> {
415
+ let cmd = format!("git {}", args.join(" "));
416
+ Command::new("sh")
417
+ .arg("-c")
418
+ .arg(&cmd) // Shell interprets args!
419
+ .output()
420
+ }
421
+ ```
422
+
423
+ ## Security Audit Command Reference
424
+
425
+ **Find potential vulnerabilities**:
426
+
427
+ ```bash
428
+ # Command injection
429
+ rg "\.arg\(\"-c\"\)" --type rust src/
430
+ rg "format!.*Command" --type rust src/
431
+
432
+ # Shell escaping
433
+ rg "\.join\(\" \"\)" --type rust src/
434
+ rg "format!.*\{.*args" --type rust src/
435
+
436
+ # Unsafe unwraps (can panic on malicious input)
437
+ rg "\.unwrap\(\)" --type rust src/
438
+
439
+ # Bounds violations
440
+ rg "\[.*\.\.\.\]" --type rust src/
441
+ rg "\[.*\.\.]" --type rust src/
442
+
443
+ # Hook security
444
+ rg "eval|source" --type bash .claude/hooks/
445
+ ```
446
+
447
+ ## Incident Response
448
+
449
+ **If vulnerability discovered**:
450
+
451
+ 1. **Assess severity**: Use CVSS scoring (Critical/High/Medium/Low)
452
+ 2. **Develop patch**: Fix vulnerability in isolated branch
453
+ 3. **Test fix**: Verify with security tests + integration tests
454
+ 4. **Release hotfix**: PATCH version bump (e.g., v0.16.0 → v0.16.1)
455
+ 5. **Disclose responsibly**: GitHub Security Advisory, CVE if applicable
456
+
457
+ **Example advisory template**:
458
+
459
+ ```markdown
460
+ ## Security Advisory: Command Injection in rtk v0.16.0
461
+
462
+ **Severity**: CRITICAL (CVSS 9.8)
463
+ **Affected versions**: v0.15.0 - v0.16.0
464
+ **Fixed in**: v0.16.1
465
+
466
+ **Description**:
467
+ RTK versions 0.15.0 through 0.16.0 are vulnerable to command injection
468
+ via malicious git repository names. An attacker can execute arbitrary
469
+ shell commands by creating a repository with special characters in the name.
470
+
471
+ **Impact**:
472
+ Remote code execution with user privileges.
473
+
474
+ **Mitigation**:
475
+ Upgrade to v0.16.1 immediately. As a workaround, avoid using RTK in
476
+ directories with untrusted repository names.
477
+
478
+ **Credits**:
479
+ Reported by: Security Researcher Name
480
+ ```
481
+
482
+ ## Security Resources
483
+
484
+ **Tools**:
485
+ - `cargo audit` - Dependency vulnerability scanning
486
+ - `cargo-geiger` - Unsafe code detection
487
+ - `cargo-deny` - Dependency policy enforcement
488
+ - `semgrep` - Static analysis for security patterns
489
+
490
+ **Run security checks**:
491
+ ```bash
492
+ # Dependency vulnerabilities
493
+ cargo install cargo-audit
494
+ cargo audit
495
+
496
+ # Unsafe code detection
497
+ cargo install cargo-geiger
498
+ cargo geiger
499
+
500
+ # Static analysis
501
+ cargo install semgrep
502
+ semgrep --config auto
503
+ ```