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)]
11    pub allow_list: Vec<String>,
12
13    /// Additional directories that should be searched/prepended to PATH for command execution
14    #[serde(default = "default_extra_path_entries")]
15    pub extra_path_entries: Vec<String>,
16
17    /// Commands that are always denied
18    #[serde(default)]
19    pub deny_list: Vec<String>,
20
21    /// Glob patterns allowed for shell commands (applies to Bash)
22    #[serde(default)]
23    pub allow_glob: Vec<String>,
24
25    /// Glob patterns denied for shell commands
26    #[serde(default)]
27    pub deny_glob: Vec<String>,
28
29    /// Regex allow patterns for shell commands
30    #[serde(default)]
31    pub allow_regex: Vec<String>,
32
33    /// Regex deny patterns for shell commands
34    #[serde(default)]
35    pub deny_regex: Vec<String>,
36}
37
38const DEFAULT_ALLOW_LIST: &[&str] = &[
39    "ls",
40    "pwd",
41    "cat",
42    "grep",
43    "find",
44    "head",
45    "tail",
46    "wc",
47    "git status",
48    "git diff",
49    "git log",
50    "git show",
51    "git branch",
52    "git remote",
53    "cargo check",
54    "cargo build",
55    "cargo build --release",
56    "cargo build --profile release",
57    "cargo test",
58    "cargo run",
59    "cargo clippy",
60    "cargo fmt",
61    "cargo tree",
62    "cargo metadata",
63    "cargo doc",
64    "cargo nextest run",
65    "cargo nextest",
66    "rustc",
67    "which",
68    "echo",
69    "printf",
70    "date",
71    "tree",
72    "stat",
73    "file",
74    "sort",
75    "uniq",
76    "cut",
77    "awk",
78    "sed",
79    "tar",
80    "zip",
81    "unzip",
82    "gzip",
83    "gunzip",
84    "make",
85    "cmake",
86    "ninja",
87    "python3",
88    "python3 -m pip install",
89    "python3 -m pytest",
90    "python3 -m build",
91    "python",
92    "pip3",
93    "pip",
94    "virtualenv",
95    "node",
96    "npm",
97    "npm run build",
98    "npm run test",
99    "npm install",
100    "yarn",
101    "yarn build",
102    "yarn test",
103    "pnpm",
104    "pnpm build",
105    "pnpm test",
106    "bun",
107    "bun install",
108    "bun run",
109    "bun test",
110    "npx",
111    "go",
112    "go build",
113    "go test",
114    "gcc",
115    "g++",
116    "clang",
117    "clang++",
118    "javac",
119    "java",
120    "mvn",
121    "gradle",
122    "docker",
123    "docker-compose",
124];
125
126impl Default for CommandsConfig {
127    fn default() -> Self {
128        Self {
129            allow_list: DEFAULT_ALLOW_LIST.iter().map(|s| (*s).into()).collect(),
130            extra_path_entries: default_extra_path_entries(),
131            deny_list: vec![
132                "rm -rf /".into(),
133                "rm -rf ~".into(),
134                "rm -rf /*".into(),
135                "rm -rf /home".into(),
136                "rm -rf /usr".into(),
137                "rm -rf /etc".into(),
138                "rm -rf /var".into(),
139                "rm -rf /opt".into(),
140                "rmdir /".into(),
141                "rmdir /home".into(),
142                "rmdir /usr".into(),
143                "shutdown".into(),
144                "reboot".into(),
145                "halt".into(),
146                "poweroff".into(),
147                "init 0".into(),
148                "init 6".into(),
149                "systemctl poweroff".into(),
150                "systemctl reboot".into(),
151                "systemctl halt".into(),
152                "sudo rm".into(),
153                "sudo chmod 777".into(),
154                "sudo chown".into(),
155                "sudo passwd".into(),
156                "sudo su".into(),
157                "sudo -i".into(),
158                "sudo bash".into(),
159                "su root".into(),
160                "su -".into(),
161                "format".into(),
162                "fdisk".into(),
163                "mkfs".into(),
164                "mkfs.ext4".into(),
165                "mkfs.xfs".into(),
166                "mkfs.vfat".into(),
167                "dd if=/dev/zero".into(),
168                "dd if=/dev/random".into(),
169                "dd if=/dev/urandom".into(),
170                "wget --no-check-certificate".into(),
171                ":(){ :|:& };:".into(), // Fork bomb
172                "nohup bash -i".into(),
173                "exec bash -i".into(),
174                "eval".into(),
175                "source /etc/bashrc".into(),
176                "source ~/.bashrc".into(),
177                "chmod 777".into(),
178                "chmod -R 777".into(),
179                "chown -R".into(),
180                "chgrp -R".into(),
181                "rm ~/.ssh/*".into(),
182                "rm -r ~/.ssh".into(),
183                "cat /etc/passwd".into(),
184                "cat /etc/shadow".into(),
185                "cat ~/.ssh/id_*".into(),
186                "tail -f /var/log".into(),
187                "head -n 1 /var/log".into(),
188            ],
189            allow_glob: vec![
190                "git *".into(),
191                "cargo *".into(),
192                "cargo nextest *".into(),
193                "rustc *".into(),
194                "python *".into(),
195                "python3 *".into(),
196                "pip *".into(),
197                "pip3 *".into(),
198                "node *".into(),
199                "npm *".into(),
200                "npm run *".into(),
201                "yarn *".into(),
202                "yarn run *".into(),
203                "pnpm *".into(),
204                "pnpm run *".into(),
205                "bun *".into(),
206                "bun run *".into(),
207                "npx *".into(),
208                "go *".into(),
209                "gcc *".into(),
210                "g++ *".into(),
211                "clang *".into(),
212                "clang++ *".into(),
213                "javac *".into(),
214                "java *".into(),
215                "mvn *".into(),
216                "gradle *".into(),
217                "make *".into(),
218                "cmake *".into(),
219                "ninja *".into(),
220                "docker *".into(),
221                "docker-compose *".into(),
222                "virtualenv *".into(),
223                "tar *".into(),
224                "zip *".into(),
225                "unzip *".into(),
226                "gzip *".into(),
227                "gunzip *".into(),
228            ],
229            deny_glob: vec![
230                "rm *".into(),
231                "sudo *".into(),
232                "chmod *".into(),
233                "chown *".into(),
234                "kill *".into(),
235                "pkill *".into(),
236                "systemctl *".into(),
237                "service *".into(),
238                "mount *".into(),
239                "umount *".into(),
240                "docker run *".into(),
241                "kubectl *".into(),
242            ],
243            allow_regex: vec![
244                r"^(ls|pwd|cat|grep|find|head|tail|wc|echo|printf|date|tree|stat|file|sort|uniq|cut|awk|sed|tar|zip|unzip|gzip|gunzip)\b".into(),
245                r"^git (status|diff|log|show|branch|remote)\b".into(),
246                r"^cargo (check|build|test|run|doc|clippy|fmt|tree|metadata|nextest)\b".into(),
247                r"^rustc\b".into(),
248                r"^(python|python3) (-m | )?\w*".into(),
249                r"^(pip|pip3)\b".into(),
250                r"^virtualenv\b".into(),
251                r"^(node|npm|yarn|pnpm|bun|npx)\b".into(),
252                r"^go\b".into(),
253                r"^(gcc|g\+\+|clang|clang\++)\b".into(),
254                r"^(javac|java)\b".into(),
255                r"^(mvn|gradle)\b".into(),
256                r"^(make|cmake)\b".into(),
257                r"^(docker|docker-compose)\b".into(),
258            ],
259            deny_regex: vec![
260                r"rm\s+(-rf|--force)".into(),
261                r"sudo\s+.*".into(),
262                r"chmod\s+.*".into(),
263                r"chown\s+.*".into(),
264                r"docker\s+run\s+.*--privileged".into(),
265                r"kubectl\s+(delete|drain|uncordon)".into(),
266            ],
267        }
268    }
269}
270
271fn default_extra_path_entries() -> Vec<String> {
272    command_constants::DEFAULT_EXTRA_PATH_ENTRIES
273        .iter()
274        .map(|value| (*value).into())
275        .collect()
276}