@@ -26,6 +26,18 @@ def _get_allowed_commands(self) -> set:
26
26
allow_commands = os .environ .get ("ALLOW_COMMANDS" , "" )
27
27
return {cmd .strip () for cmd in allow_commands .split ("," ) if cmd .strip ()}
28
28
29
+ def _clean_command (self , command : List [str ]) -> List [str ]:
30
+ """
31
+ Clean command by trimming whitespace from each part.
32
+
33
+ Args:
34
+ command (List[str]): Original command and its arguments
35
+
36
+ Returns:
37
+ List[str]: Cleaned command with trimmed whitespace
38
+ """
39
+ return [arg .strip () for arg in command if arg .strip ()]
40
+
29
41
def _validate_command (self , command : List [str ]) -> None :
30
42
"""
31
43
Validate if the command is allowed to be executed.
@@ -43,17 +55,20 @@ def _validate_command(self, command: List[str]) -> None:
43
55
if not allowed_commands :
44
56
raise ValueError ("No commands are allowed. Please set ALLOW_COMMANDS environment variable." )
45
57
46
- if command [0 ] not in allowed_commands :
47
- raise ValueError (f"Command not allowed: { command [0 ]} " )
58
+ # Clean and check the first command
59
+ cleaned_cmd = command [0 ].strip ()
60
+ if cleaned_cmd not in allowed_commands :
61
+ raise ValueError (f"Command not allowed: { cleaned_cmd } " )
48
62
49
63
# Check for shell operators and validate subsequent commands
50
64
for i , arg in enumerate (command [1 :], start = 1 ):
51
- if arg in [";" , "&&" , "||" , "|" ]:
65
+ cleaned_arg = arg .strip ()
66
+ if cleaned_arg in [";" , "&&" , "||" , "|" ]:
52
67
if i + 1 >= len (command ):
53
- raise ValueError (f"Unexpected shell operator: { arg } " )
54
- next_cmd = command [i + 1 ]
68
+ raise ValueError (f"Unexpected shell operator: { cleaned_arg } " )
69
+ next_cmd = command [i + 1 ]. strip ()
55
70
if next_cmd not in allowed_commands :
56
- raise ValueError (f"Command not allowed after { arg } : { next_cmd } " )
71
+ raise ValueError (f"Command not allowed after { cleaned_arg } : { next_cmd } " )
57
72
58
73
async def execute (self , command : List [str ], stdin : Optional [str ] = None ) -> Dict [str , Any ]:
59
74
"""
@@ -70,7 +85,12 @@ async def execute(self, command: List[str], stdin: Optional[str] = None) -> Dict
70
85
start_time = time .time ()
71
86
72
87
try :
73
- self ._validate_command (command )
88
+ # Clean command before validation and execution
89
+ cleaned_command = self ._clean_command (command )
90
+ if not cleaned_command :
91
+ raise ValueError ("Empty command" )
92
+
93
+ self ._validate_command (cleaned_command )
74
94
except ValueError as e :
75
95
return {
76
96
"error" : str (e ),
@@ -82,8 +102,8 @@ async def execute(self, command: List[str], stdin: Optional[str] = None) -> Dict
82
102
83
103
try :
84
104
process = await asyncio .create_subprocess_exec (
85
- command [0 ],
86
- * command [1 :],
105
+ cleaned_command [0 ],
106
+ * cleaned_command [1 :],
87
107
stdin = asyncio .subprocess .PIPE if stdin else None ,
88
108
stdout = asyncio .subprocess .PIPE ,
89
109
stderr = asyncio .subprocess .PIPE ,
@@ -94,17 +114,18 @@ async def execute(self, command: List[str], stdin: Optional[str] = None) -> Dict
94
114
stdout , stderr = await process .communicate (input = stdin_bytes )
95
115
96
116
return {
117
+ "error" : None , # Set error field to None for success case
97
118
"stdout" : stdout .decode () if stdout else "" ,
98
119
"stderr" : stderr .decode () if stderr else "" ,
99
120
"status" : process .returncode ,
100
121
"execution_time" : time .time () - start_time
101
122
}
102
123
except FileNotFoundError :
103
124
return {
104
- "error" : f"Command not found: { command [0 ]} " ,
125
+ "error" : f"Command not found: { cleaned_command [0 ]} " ,
105
126
"status" : 1 ,
106
127
"stdout" : "" ,
107
- "stderr" : f"Command not found: { command [0 ]} " ,
128
+ "stderr" : f"Command not found: { cleaned_command [0 ]} " ,
108
129
"execution_time" : time .time () - start_time
109
130
}
110
131
except Exception as e :
0 commit comments