@@ -10,17 +10,17 @@ use std::sync::LazyLock;
1010pub static BLOCKED_PATTERNS : LazyLock < Vec < Regex > > = LazyLock :: new ( || {
1111 vec ! [
1212 // Destructive filesystem operations
13- Regex :: new( r"rm\s+(-[rfRF]+\s+)*[/~](\s|$)" ) . unwrap( ) ,
14- Regex :: new( r"rm\s+(-[rfRF]+\s+)*/\*" ) . unwrap( ) ,
15- Regex :: new( r"rm\s+(-[rfRF]+\s+)*~" ) . unwrap( ) ,
13+ Regex :: new( r"(?m)(^|[;&|]\s*) rm\s+(-[rfRF]+\s+)*[/~](\s|$)" ) . unwrap( ) ,
14+ Regex :: new( r"(?m)(^|[;&|]\s*) rm\s+(-[rfRF]+\s+)*/\*" ) . unwrap( ) ,
15+ Regex :: new( r"(?m)(^|[;&|]\s*) rm\s+(-[rfRF]+\s+)*~" ) . unwrap( ) ,
1616 // Disk/device operations
17- Regex :: new( r"dd\s+.*if=" ) . unwrap( ) ,
18- Regex :: new( r"mkfs" ) . unwrap( ) ,
17+ Regex :: new( r"(?m)(^|[;&|]\s*) dd\s+.*if=" ) . unwrap( ) ,
18+ Regex :: new( r"(?m)(^|[;&|]\s*) mkfs" ) . unwrap( ) ,
1919 Regex :: new( r">\s*/dev/sd" ) . unwrap( ) ,
2020 Regex :: new( r">\s*/dev/nvme" ) . unwrap( ) ,
2121 // Permission bombs
22- Regex :: new( r"chmod\s+(-[rR]+\s+)*777\s+/" ) . unwrap( ) ,
23- Regex :: new( r"chown\s+(-[rR]+\s+)*.*\s+/" ) . unwrap( ) ,
22+ Regex :: new( r"(?m)(^|[;&|]\s*) chmod\s+(-[rR]+\s+)*777\s+/" ) . unwrap( ) ,
23+ Regex :: new( r"(?m)(^|[;&|]\s*) chown\s+(-[rR]+\s+)*.*\s+/" ) . unwrap( ) ,
2424 // Fork bomb
2525 Regex :: new( r":\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;" ) . unwrap( ) ,
2626 // Dangerous redirects
@@ -34,24 +34,18 @@ pub static BLOCKED_PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
3434} ) ;
3535
3636/// Commands that require extra caution (requires user confirmation).
37- pub static CAUTION_PATTERNS : LazyLock < Vec < & str > > = LazyLock :: new ( || {
37+ pub static CAUTION_PATTERNS : LazyLock < Vec < Regex > > = LazyLock :: new ( || {
3838 vec ! [
39- "sudo" ,
40- "su " ,
41- "rm " ,
42- "mv " ,
43- "chmod" ,
44- "chown" ,
45- "kill" ,
46- "pkill" ,
47- "killall" ,
48- "git push --force" ,
49- "git push -f" ,
50- "git reset --hard" ,
51- "cargo publish" ,
52- "npm publish" ,
53- "docker rm" ,
54- "docker rmi" ,
39+ // Dangerous binaries (word boundary safe)
40+ // Matches start of line or after command separator (; | &)
41+ Regex :: new( r"(?m)(^|[;&|]\s*)(sudo|su|rm|mv|chmod|chown|kill|pkill|killall)(\s+|$)" ) . unwrap( ) ,
42+
43+ // Dangerous subcommands
44+ Regex :: new( r"(?m)(^|[;&|]\s*)(docker\s+(rm|rmi)|cargo\s+publish|npm\s+publish)(\s+|$)" ) . unwrap( ) ,
45+
46+ // Dangerous flags
47+ Regex :: new( r"git\s+push\s+.*(-f|--force)" ) . unwrap( ) ,
48+ Regex :: new( r"git\s+reset\s+.*--hard" ) . unwrap( ) ,
5549 ]
5650} ) ;
5751
@@ -70,7 +64,7 @@ pub fn is_blocked(command: &str) -> Option<String> {
7064pub fn needs_caution ( command : & str ) -> bool {
7165 CAUTION_PATTERNS
7266 . iter ( )
73- . any ( |pattern| command . contains ( pattern ) )
67+ . any ( |pattern| pattern . is_match ( command ) )
7468}
7569
7670#[ cfg( test) ]
@@ -92,6 +86,12 @@ mod tests {
9286 assert ! ( is_blocked( "ls -l" ) . is_none( ) ) ;
9387 }
9488
89+ #[ test]
90+ fn test_blocked_false_positives ( ) {
91+ assert ! ( is_blocked( "echo harm -rf /" ) . is_none( ) , "Should not block 'harm'" ) ;
92+ assert ! ( is_blocked( "echo farm -rf /*" ) . is_none( ) , "Should not block 'farm'" ) ;
93+ }
94+
9595 #[ test]
9696 fn test_caution_patterns ( ) {
9797 assert ! ( needs_caution( "sudo apt update" ) ) ;
@@ -100,4 +100,21 @@ mod tests {
100100 assert ! ( needs_caution( "git reset --hard HEAD" ) ) ;
101101 assert ! ( !needs_caution( "ls -l" ) ) ;
102102 }
103+
104+ #[ test]
105+ fn test_caution_patterns_edge_cases ( ) {
106+ assert ! ( needs_caution( "rm\t file.txt" ) , "Should catch tab separator" ) ;
107+ assert ! ( needs_caution( "rm\n file.txt" ) , "Should catch newline separator" ) ;
108+ assert ! ( needs_caution( "echo hi; rm file" ) , "Should catch rm after semicolon" ) ;
109+ assert ! ( needs_caution( "echo hi | rm file" ) , "Should catch rm after pipe" ) ;
110+ assert ! ( needs_caution( "rm" ) , "Should catch bare rm" ) ;
111+ }
112+
113+ #[ test]
114+ fn test_caution_false_positives ( ) {
115+ assert ! ( !needs_caution( "echo farm animal" ) , "Should not flag 'farm' as dangerous" ) ;
116+ assert ! ( !needs_caution( "echo supper" ) , "Should not flag 'supper' (contains 'su')" ) ;
117+ assert ! ( !needs_caution( "mkdir form" ) , "Should not flag 'form' (matches 'rm' substring)" ) ;
118+ assert ! ( !needs_caution( "echo remove" ) , "Should not flag 'remove'" ) ;
119+ }
103120}
0 commit comments