1
1
package cc.unitmesh.terminal.sketch
2
2
3
- object ShellSyntaxSafetyCheck {
3
+ import com.intellij.openapi.project.Project
4
+ import com.intellij.psi.PsiFileFactory
5
+ import com.intellij.psi.util.PsiTreeUtil
6
+ import com.intellij.sh.ShLanguage
7
+ import com.intellij.sh.psi.ShCommand
8
+ import com.intellij.sh.psi.ShFile
4
9
10
+ object ShellSyntaxSafetyCheck {
5
11
/* *
6
12
* Check if shell command contains dangerous operations
7
13
* @return Pair<Boolean, String> - first: is dangerous, second: reason message
8
14
*/
9
- fun checkDangerousCommand (command : String ): Pair <Boolean , String > {
10
- val dangerousPatterns = mapOf (
11
- " \\ brm\\ s+(-[a-zA-Z]*f|-[a-zA-Z]*r|-[a-zA-Z]*(rf|fr))\\ b.*" .toRegex() to " Dangerous rm command with recursive or force flags" ,
12
- " \\ brm\\ s+-[a-zA-Z]*\\ s+/\\ b.*" .toRegex() to " Removing files from root directory" ,
13
- " \\ brmdir\\ s+/\\ b.*" .toRegex() to " Removing directories from root" ,
14
- " \\ bmkfs\\ b.*" .toRegex() to " Filesystem formatting command" ,
15
- " \\ bdd\\ b.*" .toRegex() to " Low-level disk operation" ,
16
- " \\ b:[(][)][{]\\ s*:|:&\\ s*[}];:.*" .toRegex() to " Potential fork bomb" ,
17
- " \\ bchmod\\ s+-[a-zA-Z]*R\\ b.*777\\ b.*" .toRegex() to " Recursive chmod with insecure permissions" ,
18
- " \\ bsudo\\ s+rm\\ b.*" .toRegex() to " Removing files with elevated privileges" ,
19
- )
20
-
21
- // Also catch simpler rm commands (without flags but still potentially dangerous)
22
- if (command.trim().startsWith(" rm " ) && ! command.contains(" -i" ) && ! command.contains(" --interactive" )) {
23
- return Pair (true , " Remove command detected, use with caution" )
24
- }
15
+ fun checkDangerousCommand (project : Project , command : String ): Pair <Boolean , String > {
16
+ val psiFile = PsiFileFactory .getInstance(project)
17
+ .createFileFromText(" temp.sh" , ShLanguage .INSTANCE , command) as ? ShFile
18
+ ? : return Pair (true , " Could not parse command" )
25
19
26
- for ((pattern, message) in dangerousPatterns) {
27
- if (pattern.containsMatchIn(command)) {
28
- return Pair (true , message)
20
+ val commandElements = PsiTreeUtil .findChildrenOfType(psiFile, ShCommand ::class .java)
21
+
22
+ for (cmd in commandElements) {
23
+ if (isDangerousRmCommand(cmd)) {
24
+ return Pair (true , " Dangerous rm command detected" )
29
25
}
26
+
27
+ if (isSudoCommand(cmd)) {
28
+ val sudoArgs = getSudoArgs(cmd)
29
+ if (sudoArgs.contains(" rm" )) {
30
+ return Pair (true , " Removing files with elevated privileges" )
31
+ }
32
+ }
33
+
34
+ if (isCommandWithName(cmd, " mkfs" )) {
35
+ return Pair (true , " Filesystem formatting command" )
36
+ }
37
+
38
+ if (isCommandWithName(cmd, " dd" )) {
39
+ return Pair (true , " Low-level disk operation" )
40
+ }
41
+
42
+ if (isCommandWithName(cmd, " chmod" ) && hasRecursiveFlag(cmd) && hasInsecurePermissions(cmd)) {
43
+ return Pair (true , " Recursive chmod with insecure permissions" )
44
+ }
45
+
46
+ if (operatesOnRootDirectory(cmd)) {
47
+ return Pair (true , " Operation targeting root directory" )
48
+ }
49
+ }
50
+
51
+ if (command.contains(" :(){ :|:& };:" ) || command.matches(" .*:[(][)][{]\\ s*:|:&\\ s*[}];:.*" .toRegex())) {
52
+ return Pair (true , " Potential fork bomb" )
30
53
}
31
54
32
55
return Pair (false , " " )
33
56
}
57
+
58
+ private fun isDangerousRmCommand (command : ShCommand ): Boolean {
59
+ if (! isCommandWithName(command, " rm" )) return false
60
+
61
+ val options = getCommandOptions(command)
62
+ return options.contains(" -rf" ) || options.contains(" -fr" ) ||
63
+ options.contains(" -r" ) && options.contains(" -f" ) ||
64
+ options.contains(" -f" ) && ! options.contains(" -i" )
65
+ }
66
+
67
+ private fun isSudoCommand (command : ShCommand ): Boolean {
68
+ return isCommandWithName(command, " sudo" )
69
+ }
70
+
71
+ private fun getSudoArgs (cmd : ShCommand ): List <String > {
72
+ return cmd.text.trim().split(" \\ s+" .toRegex()).drop(1 )
73
+ }
74
+
75
+ private fun getCommandOptions (cmd : ShCommand ): List <String > {
76
+ return cmd.text.trim().split(" \\ s+" .toRegex()).filter { it.startsWith(" -" ) }
77
+ }
78
+
79
+ private fun isCommandWithName (cmd : ShCommand , name : String ): Boolean {
80
+ val tokens = cmd.text.trim().split(" \\ s+" .toRegex())
81
+ return tokens.firstOrNull() == name
82
+ }
83
+
84
+ private fun hasRecursiveFlag (cmd : ShCommand ): Boolean {
85
+ return getCommandOptions(cmd).any { it.matches(" -[rR]+" .toRegex()) }
86
+ }
87
+
88
+ private fun hasInsecurePermissions (cmd : ShCommand ): Boolean {
89
+ return cmd.text.contains(" 777" )
90
+ }
91
+
92
+ private fun operatesOnRootDirectory (cmd : ShCommand ): Boolean {
93
+ val tokens = cmd.text.trim().split(" \\ s+" .toRegex())
94
+ return tokens.any { it == " /" }
95
+ }
34
96
}
0 commit comments