- Replace shell=True with shell=False + shlex.split() - Add command whitelist to restrict allowed executables - Parse commands safely to prevent metacharacter injection (;, &&, |, backticks)
135 lines
3.9 KiB
Python
135 lines
3.9 KiB
Python
"""
|
|
Command line execution tool for Clover - A terminal assistant for AI-powered project management
|
|
"""
|
|
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
|
|
# Whitelist of allowed commands for safe execution
|
|
ALLOWED_COMMANDS = {
|
|
"ls", "cat", "echo", "pwd", "date", "whoami", "id",
|
|
"git", "python", "python3", "node", "npm", "pip", "pip3",
|
|
"mkdir", "cp", "mv", "rm", "touch", "chmod", "chown",
|
|
"grep", "find", "head", "tail", "wc", "sort", "uniq", "diff",
|
|
"make", "cmake", "cargo", "go", "rustc",
|
|
"docker", "docker-compose",
|
|
"curl", "wget",
|
|
"ps", "top", "df", "free", "uname",
|
|
"which", "whereis", "type",
|
|
"test", "stat", "file",
|
|
"bash", "sh", "zsh",
|
|
"vim", "nano", "less", "more",
|
|
"tar", "zip", "unzip", "gzip", "gunzip",
|
|
"sed", "awk", "tr", "cut", "paste", "join", "comm",
|
|
"xargs", "tee", "yes", "seq", "bc",
|
|
}
|
|
|
|
|
|
def commandline(command, allow_execution=True):
|
|
"""
|
|
Execute a system command with user permission.
|
|
|
|
Uses shell=False with shlex.split() to prevent command injection.
|
|
The first word of the command must be in the ALLOWED_COMMANDS whitelist.
|
|
|
|
Args:
|
|
command (str): The command to execute
|
|
allow_execution (bool): Whether execution is allowed (default: True)
|
|
|
|
Returns:
|
|
str: Output of the command or permission prompt
|
|
|
|
Raises:
|
|
PermissionError: If execution is not permitted or command not whitelisted
|
|
subprocess.CalledProcessError: If command execution fails
|
|
"""
|
|
if not allow_execution:
|
|
return f"Command execution denied. Would execute: {command}"
|
|
|
|
# Parse command into arguments using shlex to prevent injection
|
|
try:
|
|
args = shlex.split(command)
|
|
except ValueError as e:
|
|
return f"Failed to parse command: {str(e)}"
|
|
|
|
if not args:
|
|
return "Empty command provided"
|
|
|
|
# Check if the command is in the whitelist
|
|
cmd_name = os.path.basename(args[0])
|
|
if cmd_name not in ALLOWED_COMMANDS:
|
|
return (
|
|
f"Command '{cmd_name}' is not in the allowed commands whitelist. "
|
|
f"Allowed commands: {', '.join(sorted(ALLOWED_COMMANDS))}"
|
|
)
|
|
|
|
try:
|
|
print(f"Executing command: {command}")
|
|
|
|
# Execute the command with shell=False to prevent injection
|
|
result = subprocess.run(
|
|
args,
|
|
shell=False,
|
|
check=True,
|
|
text=True,
|
|
capture_output=True,
|
|
timeout=300, # 5 minute timeout
|
|
)
|
|
|
|
return result.stdout
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
error_msg = f"Command failed with return code {e.returncode}\n"
|
|
if e.stderr:
|
|
error_msg += f"Error output: {e.stderr}"
|
|
return error_msg
|
|
|
|
except subprocess.TimeoutExpired:
|
|
return "Command timed out after 300 seconds"
|
|
|
|
except Exception as e:
|
|
return f"Error executing command '{command}': {str(e)}"
|
|
|
|
|
|
def safe_execute(command, permission_prompt=True):
|
|
"""
|
|
Safely execute a system command with optional permission prompt.
|
|
|
|
Args:
|
|
command (str): The command to execute
|
|
permission_prompt (bool): Whether to prompt for permission
|
|
|
|
Returns:
|
|
str: Command output or permission denial message
|
|
"""
|
|
if permission_prompt:
|
|
print(f"Permission needed to run: {command}")
|
|
response = input("Allow execution? (y/N): ")
|
|
if response.lower() not in ["y", "yes"]:
|
|
return "Execution denied by user"
|
|
|
|
return commandline(command)
|
|
|
|
|
|
# Example usage function
|
|
def example_usage():
|
|
"""
|
|
Example of how to use the command line tool.
|
|
"""
|
|
print("Command Line Tool Examples:")
|
|
|
|
# Example 1: Safe execution with prompt
|
|
result = safe_execute("echo 'Hello, Clover!'")
|
|
print(f"Result: {result}")
|
|
|
|
# Example 2: Direct execution
|
|
result = commandline("ls -la")
|
|
print(f"Directory listing: {result}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
example_usage()
|