1use serde::{Deserialize, Serialize};
2
3use crate::constants::commands as command_constants;
4
5#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
7#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct CommandsConfig {
9 #[serde(default = "default_allow_list")]
11 pub allow_list: Vec<String>,
12
13 #[serde(default)]
15 pub approval_prefixes: Vec<String>,
16
17 #[serde(default = "default_extra_path_entries")]
19 pub extra_path_entries: Vec<String>,
20
21 #[serde(default = "default_deny_list")]
23 pub deny_list: Vec<String>,
24
25 #[serde(default = "default_allow_glob")]
27 pub allow_glob: Vec<String>,
28
29 #[serde(default = "default_deny_glob")]
31 pub deny_glob: Vec<String>,
32
33 #[serde(default = "default_allow_regex")]
35 pub allow_regex: Vec<String>,
36
37 #[serde(default = "default_deny_regex")]
39 pub deny_regex: Vec<String>,
40}
41
42const DEFAULT_ALLOW_LIST: &[&str] = &[
43 "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 "tar",
62 "zip",
63 "unzip",
64 "gzip",
65 "gunzip",
66 "make",
68 "cmake",
69 "ninja",
70 "which",
71 "echo",
72 "printf",
73 "read",
74 "date",
75 "sleep",
76 "git status",
78 "git diff",
79 "git log",
80 "git show",
81 "git branch",
82 "git remote",
83 "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 "python3",
98 "python3 -m pip install",
99 "python3 -m pytest",
100 "python3 -m build",
101 "python",
102 "pip3",
103 "pip",
104 "virtualenv",
105 "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",
124 "go build",
125 "go test",
126 "gcc",
128 "g++",
129 "clang",
130 "clang++",
131 "javac",
133 "java",
134 "mvn",
135 "gradle",
136 "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
156fn default_allow_list() -> Vec<String> {
158 DEFAULT_ALLOW_LIST.iter().map(|s| (*s).into()).collect()
159}
160
161fn default_deny_list() -> Vec<String> {
163 vec![
164 "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 "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 "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 "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 "wget --no-check-certificate".into(),
209 ":(){ :|:& };:".into(), "nohup bash -i".into(),
211 "exec bash -i".into(),
212 "eval".into(),
213 "source /etc/bashrc".into(),
215 "source ~/.bashrc".into(),
216 "chmod 777".into(),
218 "chmod -R 777".into(),
219 "chown -R".into(),
220 "chgrp -R".into(),
221 "rm ~/.ssh/*".into(),
223 "rm -r ~/.ssh".into(),
224 "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
233fn default_allow_glob() -> Vec<String> {
235 vec![
236 "git *".into(),
238 "cargo *".into(),
240 "rustc *".into(),
241 "python *".into(),
243 "python3 *".into(),
244 "pip *".into(),
245 "pip3 *".into(),
246 "virtualenv *".into(),
247 "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 *".into(),
260 "gcc *".into(),
262 "g++ *".into(),
263 "clang *".into(),
264 "clang++ *".into(),
265 "javac *".into(),
267 "java *".into(),
268 "mvn *".into(),
269 "gradle *".into(),
270 "make *".into(),
272 "cmake *".into(),
273 "ninja *".into(),
274 "docker *".into(),
276 "docker-compose *".into(),
277 "tar *".into(),
279 "zip *".into(),
280 "unzip *".into(),
281 "gzip *".into(),
282 "gunzip *".into(),
283 ]
284}
285
286fn default_deny_glob() -> Vec<String> {
288 vec![
289 "rm *".into(),
291 "sudo *".into(),
293 "chmod *".into(),
295 "chown *".into(),
296 "kill *".into(),
298 "pkill *".into(),
299 "systemctl *".into(),
301 "service *".into(),
302 "mount *".into(),
304 "umount *".into(),
305 "docker run *".into(),
307 "kubectl *".into(),
309 ]
310}
311
312fn default_allow_regex() -> Vec<String> {
314 vec![
315 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 r"^git (status|diff|log|show|branch|remote)\b".into(),
319 r"^cargo (check|build|test|run|doc|clippy|fmt|tree|metadata)\b".into(),
321 r"^rustc\b".into(),
322 r"^(python|python3) (-m | )?\w*".into(),
324 r"^(pip|pip3)\b".into(),
325 r"^virtualenv\b".into(),
326 r"^(node|npm|yarn|pnpm|bun|npx)\b".into(),
328 r"^go\b".into(),
330 r"^(gcc|g\+\+|clang|clang\++)\b".into(),
332 r"^(javac|java)\b".into(),
334 r"^(mvn|gradle)\b".into(),
335 r"^(make|cmake)\b".into(),
337 r"^(docker|docker-compose)\b".into(),
339 ]
340}
341
342fn default_deny_regex() -> Vec<String> {
344 vec![
345 r"rm\s+(-rf|--force)".into(),
347 r"sudo\s+.*".into(),
349 r"chmod\s+.*".into(),
351 r"chown\s+.*".into(),
352 r"docker\s+run\s+.*--privileged".into(),
354 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}