@@ -28,40 +28,76 @@ import type {
2828 * Shell metacharacters that enable chaining, piping, substitution, or redirection.
2929 * All legitimate install commands are simple single commands that don't need these.
3030 */
31- const SHELL_METACHARACTER_PATTERNS : Array < { pattern : string ; label : string } > = [
32- { pattern : ";" , label : "command chaining (;)" } ,
33- // Check multi-char operators before single `|` so labels are accurate
34- { pattern : "&&" , label : "command chaining (&&)" } ,
35- { pattern : "||" , label : "command chaining (||)" } ,
36- { pattern : "|" , label : "piping (|)" } ,
37- { pattern : "`" , label : "command substitution (`)" } ,
38- { pattern : "$(" , label : "command substitution ($()" } ,
39- { pattern : "\n" , label : "newline" } ,
40- { pattern : "\r" , label : "carriage return" } ,
41- ] ;
31+ const SHELL_METACHARACTER_PATTERNS : Array < { pattern : string ; label : string } > =
32+ [
33+ { pattern : ";" , label : "command chaining (;)" } ,
34+ // Check multi-char operators before single `|` so labels are accurate
35+ { pattern : "&&" , label : "command chaining (&&)" } ,
36+ { pattern : "||" , label : "command chaining (||)" } ,
37+ { pattern : "|" , label : "piping (|)" } ,
38+ { pattern : "`" , label : "command substitution (`)" } ,
39+ { pattern : "$(" , label : "command substitution ($()" } ,
40+ { pattern : "\n" , label : "newline" } ,
41+ { pattern : "\r" , label : "carriage return" } ,
42+ ] ;
43+
44+ const WHITESPACE_RE = / \s + / ;
4245
4346/**
4447 * Executables that should never appear in a package install command.
4548 */
4649const BLOCKED_EXECUTABLES = new Set ( [
4750 // Destructive
48- "rm" , "rmdir" , "del" ,
51+ "rm" ,
52+ "rmdir" ,
53+ "del" ,
4954 // Network/exfil
50- "curl" , "wget" , "nc" , "ncat" , "netcat" , "socat" , "telnet" , "ftp" ,
55+ "curl" ,
56+ "wget" ,
57+ "nc" ,
58+ "ncat" ,
59+ "netcat" ,
60+ "socat" ,
61+ "telnet" ,
62+ "ftp" ,
5163 // Privilege escalation
52- "sudo" , "su" , "doas" ,
64+ "sudo" ,
65+ "su" ,
66+ "doas" ,
5367 // Permissions
54- "chmod" , "chown" , "chgrp" ,
68+ "chmod" ,
69+ "chown" ,
70+ "chgrp" ,
5571 // Process/system
56- "kill" , "killall" , "pkill" , "shutdown" , "reboot" , "halt" , "poweroff" ,
72+ "kill" ,
73+ "killall" ,
74+ "pkill" ,
75+ "shutdown" ,
76+ "reboot" ,
77+ "halt" ,
78+ "poweroff" ,
5779 // Disk
58- "dd" , "mkfs" , "fdisk" , "mount" , "umount" ,
80+ "dd" ,
81+ "mkfs" ,
82+ "fdisk" ,
83+ "mount" ,
84+ "umount" ,
5985 // Remote access
60- "ssh" , "scp" , "sftp" ,
86+ "ssh" ,
87+ "scp" ,
88+ "sftp" ,
6189 // Shells
62- "bash" , "sh" , "zsh" , "fish" , "csh" , "dash" ,
90+ "bash" ,
91+ "sh" ,
92+ "zsh" ,
93+ "fish" ,
94+ "csh" ,
95+ "dash" ,
6396 // Misc dangerous
64- "eval" , "exec" , "env" , "xargs" ,
97+ "eval" ,
98+ "exec" ,
99+ "env" ,
100+ "xargs" ,
65101] ) ;
66102
67103/**
@@ -77,16 +113,16 @@ export function validateCommand(command: string): string | undefined {
77113 }
78114
79115 // Layer 2: Block dangerous executables
80- const firstToken = command . trimStart ( ) . split ( / \s + / ) [ 0 ] ;
116+ const firstToken = command . trimStart ( ) . split ( WHITESPACE_RE ) [ 0 ] ;
81117 if ( ! firstToken ) {
82- return ` Blocked command: empty command` ;
118+ return " Blocked command: empty command" ;
83119 }
84120 const executable = path . basename ( firstToken ) ;
85121 if ( BLOCKED_EXECUTABLES . has ( executable ) ) {
86122 return `Blocked command: disallowed executable "${ executable } " — "${ command } "` ;
87123 }
88124
89- return undefined ;
125+ return ;
90126}
91127
92128/**
0 commit comments