Skip to main content

vtcode_config/core/
commands.rs

1use serde::{Deserialize, Serialize};
2
3use crate::constants::commands as command_constants;
4
5/// Command execution configuration
6#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
7#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct CommandsConfig {
9    /// Commands that can be executed without prompting
10    #[serde(default = "default_allow_list")]
11    pub allow_list: Vec<String>,
12
13    /// Command prefixes that skip future shell approval prompts when matched
14    #[serde(default)]
15    pub approval_prefixes: Vec<String>,
16
17    /// Additional directories that should be searched/prepended to PATH for command execution
18    #[serde(default = "default_extra_path_entries")]
19    pub extra_path_entries: Vec<String>,
20
21    /// Commands that are always denied
22    #[serde(default = "default_deny_list")]
23    pub deny_list: Vec<String>,
24
25    /// Glob patterns allowed for shell commands (applies to Bash)
26    #[serde(default = "default_allow_glob")]
27    pub allow_glob: Vec<String>,
28
29    /// Glob patterns denied for shell commands
30    #[serde(default = "default_deny_glob")]
31    pub deny_glob: Vec<String>,
32
33    /// Regex allow patterns for shell commands
34    #[serde(default = "default_allow_regex")]
35    pub allow_regex: Vec<String>,
36
37    /// Regex deny patterns for shell commands
38    #[serde(default = "default_deny_regex")]
39    pub deny_regex: Vec<String>,
40}
41
42const DEFAULT_ALLOW_LIST: &[&str] = &[
43    // File and directory operations
44    "ls",
45    "pwd",
46    "cat",
47    "grep",
48    "find",
49    "head",
50    "tail",
51    "wc",
52    "tree",
53    "stat",
54    "file",
55    "sort",
56    "uniq",
57    "cut",
58    "awk",
59    "sed",
60    // Archive operations
61    "tar",
62    "zip",
63    "unzip",
64    "gzip",
65    "gunzip",
66    // Build tools
67    "make",
68    "cmake",
69    "ninja",
70    "which",
71    "echo",
72    "printf",
73    "read",
74    "date",
75    "sleep",
76    // Version control
77    "git status",
78    "git diff",
79    "git log",
80    "git show",
81    "git branch",
82    "git remote",
83    // Rust ecosystem
84    "cargo check",
85    "cargo build",
86    "cargo build --release",
87    "cargo build --profile release",
88    "cargo test",
89    "cargo run",
90    "cargo clippy",
91    "cargo fmt",
92    "cargo tree",
93    "cargo metadata",
94    "cargo doc",
95    "rustc",
96    // Python ecosystem
97    "python3",
98    "python3 -m pip install",
99    "python3 -m pytest",
100    "python3 -m build",
101    "python",
102    "pip3",
103    "pip",
104    "virtualenv",
105    // Node.js ecosystem
106    "node",
107    "npm",
108    "npm run build",
109    "npm run test",
110    "npm install",
111    "yarn",
112    "yarn build",
113    "yarn test",
114    "pnpm",
115    "pnpm build",
116    "pnpm test",
117    "bun",
118    "bun install",
119    "bun run",
120    "bun test",
121    "npx",
122    // Go ecosystem
123    "go",
124    "go build",
125    "go test",
126    // C/C++
127    "gcc",
128    "g++",
129    "clang",
130    "clang++",
131    // Java ecosystem
132    "javac",
133    "java",
134    "mvn",
135    "gradle",
136    // Container operations
137    "docker",
138    "docker-compose",
139];
140
141impl Default for CommandsConfig {
142    fn default() -> Self {
143        Self {
144            allow_list: default_allow_list(),
145            approval_prefixes: Vec::new(),
146            extra_path_entries: default_extra_path_entries(),
147            deny_list: default_deny_list(),
148            allow_glob: default_allow_glob(),
149            deny_glob: default_deny_glob(),
150            allow_regex: default_allow_regex(),
151            deny_regex: default_deny_regex(),
152        }
153    }
154}
155
156/// Built-in `allow_list` applied when the key is absent from a `[commands]` section.
157fn default_allow_list() -> Vec<String> {
158    DEFAULT_ALLOW_LIST.iter().map(|s| (*s).into()).collect()
159}
160
161/// Built-in `deny_list` applied when the key is absent from a `[commands]` section.
162fn default_deny_list() -> Vec<String> {
163    vec![
164        // Dangerous file deletion
165        "rm".into(),
166        "rm -rf /".into(),
167        "rm -rf ~".into(),
168        "rm -rf /*".into(),
169        "rm -rf /home".into(),
170        "rm -rf /usr".into(),
171        "rm -rf /etc".into(),
172        "rm -rf /var".into(),
173        "rm -rf /opt".into(),
174        "rmdir /".into(),
175        "rmdir /home".into(),
176        "rmdir /usr".into(),
177        // System control
178        "shutdown".into(),
179        "reboot".into(),
180        "halt".into(),
181        "poweroff".into(),
182        "init 0".into(),
183        "init 6".into(),
184        "systemctl poweroff".into(),
185        "systemctl reboot".into(),
186        "systemctl halt".into(),
187        // Privilege escalation
188        "sudo rm".into(),
189        "sudo chmod 777".into(),
190        "sudo chown".into(),
191        "sudo passwd".into(),
192        "sudo su".into(),
193        "sudo -i".into(),
194        "sudo bash".into(),
195        "su root".into(),
196        "su -".into(),
197        // Disk operations
198        "format".into(),
199        "fdisk".into(),
200        "mkfs".into(),
201        "mkfs.ext4".into(),
202        "mkfs.xfs".into(),
203        "mkfs.vfat".into(),
204        "dd if=/dev/zero".into(),
205        "dd if=/dev/random".into(),
206        "dd if=/dev/urandom".into(),
207        // Security risks
208        "wget --no-check-certificate".into(),
209        ":(){ :|:& };:".into(), // Fork bomb
210        "nohup bash -i".into(),
211        "exec bash -i".into(),
212        "eval".into(),
213        // Shell configuration
214        "source /etc/bashrc".into(),
215        "source ~/.bashrc".into(),
216        // Permission changes
217        "chmod 777".into(),
218        "chmod -R 777".into(),
219        "chown -R".into(),
220        "chgrp -R".into(),
221        // SSH key destruction
222        "rm ~/.ssh/*".into(),
223        "rm -r ~/.ssh".into(),
224        // Sensitive file access
225        "cat /etc/passwd".into(),
226        "cat /etc/shadow".into(),
227        "cat ~/.ssh/id_*".into(),
228        "tail -f /var/log".into(),
229        "head -n 1 /var/log".into(),
230    ]
231}
232
233/// Built-in `allow_glob` applied when the key is absent from a `[commands]` section.
234fn default_allow_glob() -> Vec<String> {
235    vec![
236        // Version control
237        "git *".into(),
238        // Rust ecosystem
239        "cargo *".into(),
240        "rustc *".into(),
241        // Python ecosystem
242        "python *".into(),
243        "python3 *".into(),
244        "pip *".into(),
245        "pip3 *".into(),
246        "virtualenv *".into(),
247        // Node.js ecosystem
248        "node *".into(),
249        "npm *".into(),
250        "npm run *".into(),
251        "yarn *".into(),
252        "yarn run *".into(),
253        "pnpm *".into(),
254        "pnpm run *".into(),
255        "bun *".into(),
256        "bun run *".into(),
257        "npx *".into(),
258        // Go
259        "go *".into(),
260        // C/C++
261        "gcc *".into(),
262        "g++ *".into(),
263        "clang *".into(),
264        "clang++ *".into(),
265        // Java
266        "javac *".into(),
267        "java *".into(),
268        "mvn *".into(),
269        "gradle *".into(),
270        // Build tools
271        "make *".into(),
272        "cmake *".into(),
273        "ninja *".into(),
274        // Containers
275        "docker *".into(),
276        "docker-compose *".into(),
277        // Archive tools
278        "tar *".into(),
279        "zip *".into(),
280        "unzip *".into(),
281        "gzip *".into(),
282        "gunzip *".into(),
283    ]
284}
285
286/// Built-in `deny_glob` applied when the key is absent from a `[commands]` section.
287fn default_deny_glob() -> Vec<String> {
288    vec![
289        // File deletion
290        "rm *".into(),
291        // Privilege escalation
292        "sudo *".into(),
293        // Permission changes
294        "chmod *".into(),
295        "chown *".into(),
296        // Process termination
297        "kill *".into(),
298        "pkill *".into(),
299        // System services
300        "systemctl *".into(),
301        "service *".into(),
302        // Mount operations
303        "mount *".into(),
304        "umount *".into(),
305        // Dangerous container operations
306        "docker run *".into(),
307        // Kubernetes (admin access)
308        "kubectl *".into(),
309    ]
310}
311
312/// Built-in `allow_regex` applied when the key is absent from a `[commands]` section.
313fn default_allow_regex() -> Vec<String> {
314    vec![
315                // File and text utilities
316                r"^(ls|pwd|cat|grep|find|head|tail|wc|echo|printf|read|date|sleep|tree|stat|file|sort|uniq|cut|awk|sed|tar|zip|unzip|gzip|gunzip)\b".into(),
317                // Version control
318                r"^git (status|diff|log|show|branch|remote)\b".into(),
319                // Rust
320                r"^cargo (check|build|test|run|doc|clippy|fmt|tree|metadata)\b".into(),
321                r"^rustc\b".into(),
322                // Python
323                r"^(python|python3) (-m | )?\w*".into(),
324                r"^(pip|pip3)\b".into(),
325                r"^virtualenv\b".into(),
326                // Node.js
327                r"^(node|npm|yarn|pnpm|bun|npx)\b".into(),
328                // Go
329                r"^go\b".into(),
330                // C/C++
331                r"^(gcc|g\+\+|clang|clang\++)\b".into(),
332                // Java
333                r"^(javac|java)\b".into(),
334                r"^(mvn|gradle)\b".into(),
335                // Build tools
336                r"^(make|cmake)\b".into(),
337                // Containers
338                r"^(docker|docker-compose)\b".into(),
339            ]
340}
341
342/// Built-in `deny_regex` applied when the key is absent from a `[commands]` section.
343fn default_deny_regex() -> Vec<String> {
344    vec![
345        // Force removal
346        r"rm\s+(-rf|--force)".into(),
347        // Sudo commands
348        r"sudo\s+.*".into(),
349        // Permission changes
350        r"chmod\s+.*".into(),
351        r"chown\s+.*".into(),
352        // Privileged containers
353        r"docker\s+run\s+.*--privileged".into(),
354        // Dangerous kubectl operations
355        r"kubectl\s+(delete|drain|uncordon)".into(),
356    ]
357}
358
359fn default_extra_path_entries() -> Vec<String> {
360    command_constants::DEFAULT_EXTRA_PATH_ENTRIES
361        .iter()
362        .map(|value| (*value).into())
363        .collect()
364}