From a916e3bfbfb6494f06e48d43d97d2dc06b4d107c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 23 Dec 2025 10:00:35 +0000 Subject: [PATCH 1/5] Add command validation to prevent dangerous commands Co-authored-by: zhouhua258 --- src/remoteshell_mcp/command_validator.py | 115 +++++++++++++++++++++++ src/remoteshell_mcp/server.py | 12 +++ 2 files changed, 127 insertions(+) create mode 100644 src/remoteshell_mcp/command_validator.py diff --git a/src/remoteshell_mcp/command_validator.py b/src/remoteshell_mcp/command_validator.py new file mode 100644 index 0000000..66658f8 --- /dev/null +++ b/src/remoteshell_mcp/command_validator.py @@ -0,0 +1,115 @@ +"""Command validator to prevent execution of dangerous commands.""" + +import re +from typing import List, Tuple + + +class DangerousCommandError(Exception): + """Raised when a dangerous command is detected.""" + pass + + +class CommandValidator: + """Validates commands to prevent execution of dangerous operations.""" + + # 危险命令模式列表:每个元组包含 (模式, 描述) + DANGEROUS_PATTERNS: List[Tuple[str, str]] = [ + # 删除根目录(精确匹配) + (r'\brm\s+.*-.*rf\s+/\s*$', '删除根目录'), + (r'\brm\s+.*-.*rf\s+/\s+', '删除根目录'), + (r'\brm\s+.*-.*rf\s+/\*', '删除根目录下所有文件'), + (r'\brm\s+.*-.*rf\s+/\s*\*', '删除根目录下所有文件'), + + # 删除系统关键目录(精确匹配,只匹配目录本身,不匹配目录下的文件) + (r'\brm\s+.*-.*rf\s+/root(?:\s|$)', '删除/root目录'), + (r'\brm\s+.*-.*rf\s+/etc(?:\s|$)', '删除/etc目录'), + (r'\brm\s+.*-.*rf\s+/usr(?:\s|$)', '删除/usr目录'), + (r'\brm\s+.*-.*rf\s+/bin(?:\s|$)', '删除/bin目录'), + (r'\brm\s+.*-.*rf\s+/sbin(?:\s|$)', '删除/sbin目录'), + (r'\brm\s+.*-.*rf\s+/lib(?:\s|$)', '删除/lib目录'), + (r'\brm\s+.*-.*rf\s+/var(?:\s|$)', '删除/var目录'), + (r'\brm\s+.*-.*rf\s+/sys(?:\s|$)', '删除/sys目录'), + (r'\brm\s+.*-.*rf\s+/proc(?:\s|$)', '删除/proc目录'), + (r'\brm\s+.*-.*rf\s+/dev(?:\s|$)', '删除/dev目录'), + (r'\brm\s+.*-.*rf\s+/boot(?:\s|$)', '删除/boot目录'), + + # 格式化命令 + (r'\bmkfs\b', '格式化文件系统'), + (r'\bfdisk\b', '磁盘分区操作'), + (r'\bparted\b', '磁盘分区操作'), + + # 破坏性dd命令 + (r'\bdd\s+.*if=.*of=/dev/', '破坏性dd命令'), + (r'\bdd\s+.*if=/dev/zero', '使用/dev/zero的dd命令'), + (r'\bdd\s+.*if=/dev/urandom', '使用/dev/urandom的dd命令'), + + # 系统关键操作 + (r'\bchmod\s+.*777\s+.*/', '修改根目录权限'), + (r'\bchown\s+.*root\s+.*/', '修改根目录所有者'), + + # 其他危险操作 + (r'>\s*/dev/', '重定向到设备文件'), + (r':\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;', 'Fork炸弹'), + (r'\bhalt\b', '系统关机'), + (r'\bpoweroff\b', '系统关机'), + (r'\breboot\b', '系统重启'), + (r'\bshutdown\b', '系统关机'), + + # 删除所有文件的模式(但允许相对路径如 ./test) + (r'\brm\s+.*-.*rf\s+\*', '删除当前目录所有文件'), + (r'\brm\s+.*-.*rf\s+\.\.(?:\s|$|/)', '删除上级目录'), + ] + + # 允许的危险命令白名单(如果需要的话,可以在这里添加例外情况) + ALLOWED_PATTERNS: List[str] = [ + # 可以在这里添加允许的模式 + ] + + @classmethod + def validate(cls, command: str) -> None: + """ + 验证命令是否安全。 + + Args: + command: 要执行的命令字符串 + + Raises: + DangerousCommandError: 如果检测到危险命令 + """ + if not command or not command.strip(): + return + + # 标准化命令:去除多余空格,转换为小写进行匹配 + normalized_command = ' '.join(command.split()) + command_lower = normalized_command.lower() + + # 检查是否匹配白名单 + for allowed_pattern in cls.ALLOWED_PATTERNS: + if re.search(allowed_pattern, command_lower, re.IGNORECASE): + return + + # 检查是否匹配危险模式 + for pattern, description in cls.DANGEROUS_PATTERNS: + if re.search(pattern, command_lower, re.IGNORECASE): + raise DangerousCommandError( + f"检测到危险命令: {description}\n" + f"命令: {command}\n" + f"模式: {pattern}" + ) + + # 额外检查:防止通过变量或引号绕过检查 + # 检查命令中是否包含明显的危险操作(使用单词边界确保精确匹配) + dangerous_keywords_patterns = [ + (r'\brm\s+-rf\s+/\s*$', 'rm -rf /'), + (r'\brm\s+-rf\s+/\s+', 'rm -rf /'), + (r'\brm\s+-rf\s+/\*', 'rm -rf /*'), + (r'\brm\s+-rf\s+/root\b', 'rm -rf /root'), + (r'\bdd\s+.*if=/dev/zero\b', 'dd if=/dev/zero'), + ] + + for pattern, keyword_desc in dangerous_keywords_patterns: + if re.search(pattern, command_lower, re.IGNORECASE): + raise DangerousCommandError( + f"检测到危险命令关键词: {keyword_desc}\n" + f"命令: {command}" + ) diff --git a/src/remoteshell_mcp/server.py b/src/remoteshell_mcp/server.py index 823a5de..72dc1ce 100644 --- a/src/remoteshell_mcp/server.py +++ b/src/remoteshell_mcp/server.py @@ -7,6 +7,7 @@ from .config_loader import ConfigLoader, ConnectionConfig from .connection_manager import ConnectionManager from .ssh_client import SSHConnectionError, SSHCommandError, SSHFileTransferError +from .command_validator import CommandValidator, DangerousCommandError # Initialize FastMCP server @@ -107,6 +108,9 @@ def execute_command( manager = get_connection_manager() try: + # Validate command before execution + CommandValidator.validate(command) + # Get or create connection client = manager.get_or_create_connection(connection_id) @@ -126,6 +130,14 @@ def execute_command( "exit_code": result["exit_code"] } + except DangerousCommandError as e: + return { + "success": False, + "error": str(e), + "connection_id": connection_id, + "command": command, + "message": f"命令验证失败: {e}" + } except (ValueError, SSHConnectionError, SSHCommandError) as e: return { "success": False, From 2907a9f8fbc607d2e2b820801b4c3f15346eeabf Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 23 Dec 2025 11:30:50 +0000 Subject: [PATCH 2/5] Translate dangerous command error messages to English Co-authored-by: zhouhua258 --- src/remoteshell_mcp/command_validator.py | 108 +++++++++++------------ src/remoteshell_mcp/server.py | 2 +- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/remoteshell_mcp/command_validator.py b/src/remoteshell_mcp/command_validator.py index 66658f8..43877a9 100644 --- a/src/remoteshell_mcp/command_validator.py +++ b/src/remoteshell_mcp/command_validator.py @@ -12,93 +12,93 @@ class DangerousCommandError(Exception): class CommandValidator: """Validates commands to prevent execution of dangerous operations.""" - # 危险命令模式列表:每个元组包含 (模式, 描述) + # List of dangerous command patterns: each tuple contains (pattern, description) DANGEROUS_PATTERNS: List[Tuple[str, str]] = [ - # 删除根目录(精确匹配) - (r'\brm\s+.*-.*rf\s+/\s*$', '删除根目录'), - (r'\brm\s+.*-.*rf\s+/\s+', '删除根目录'), - (r'\brm\s+.*-.*rf\s+/\*', '删除根目录下所有文件'), - (r'\brm\s+.*-.*rf\s+/\s*\*', '删除根目录下所有文件'), + # Remove root directory (exact match) + (r'\brm\s+.*-.*rf\s+/\s*$', 'Remove root directory'), + (r'\brm\s+.*-.*rf\s+/\s+', 'Remove root directory'), + (r'\brm\s+.*-.*rf\s+/\*', 'Remove all files under root directory'), + (r'\brm\s+.*-.*rf\s+/\s*\*', 'Remove all files under root directory'), - # 删除系统关键目录(精确匹配,只匹配目录本身,不匹配目录下的文件) - (r'\brm\s+.*-.*rf\s+/root(?:\s|$)', '删除/root目录'), - (r'\brm\s+.*-.*rf\s+/etc(?:\s|$)', '删除/etc目录'), - (r'\brm\s+.*-.*rf\s+/usr(?:\s|$)', '删除/usr目录'), - (r'\brm\s+.*-.*rf\s+/bin(?:\s|$)', '删除/bin目录'), - (r'\brm\s+.*-.*rf\s+/sbin(?:\s|$)', '删除/sbin目录'), - (r'\brm\s+.*-.*rf\s+/lib(?:\s|$)', '删除/lib目录'), - (r'\brm\s+.*-.*rf\s+/var(?:\s|$)', '删除/var目录'), - (r'\brm\s+.*-.*rf\s+/sys(?:\s|$)', '删除/sys目录'), - (r'\brm\s+.*-.*rf\s+/proc(?:\s|$)', '删除/proc目录'), - (r'\brm\s+.*-.*rf\s+/dev(?:\s|$)', '删除/dev目录'), - (r'\brm\s+.*-.*rf\s+/boot(?:\s|$)', '删除/boot目录'), + # Remove critical system directories (exact match, only matches directory itself, not files under it) + (r'\brm\s+.*-.*rf\s+/root(?:\s|$)', 'Remove /root directory'), + (r'\brm\s+.*-.*rf\s+/etc(?:\s|$)', 'Remove /etc directory'), + (r'\brm\s+.*-.*rf\s+/usr(?:\s|$)', 'Remove /usr directory'), + (r'\brm\s+.*-.*rf\s+/bin(?:\s|$)', 'Remove /bin directory'), + (r'\brm\s+.*-.*rf\s+/sbin(?:\s|$)', 'Remove /sbin directory'), + (r'\brm\s+.*-.*rf\s+/lib(?:\s|$)', 'Remove /lib directory'), + (r'\brm\s+.*-.*rf\s+/var(?:\s|$)', 'Remove /var directory'), + (r'\brm\s+.*-.*rf\s+/sys(?:\s|$)', 'Remove /sys directory'), + (r'\brm\s+.*-.*rf\s+/proc(?:\s|$)', 'Remove /proc directory'), + (r'\brm\s+.*-.*rf\s+/dev(?:\s|$)', 'Remove /dev directory'), + (r'\brm\s+.*-.*rf\s+/boot(?:\s|$)', 'Remove /boot directory'), - # 格式化命令 - (r'\bmkfs\b', '格式化文件系统'), - (r'\bfdisk\b', '磁盘分区操作'), - (r'\bparted\b', '磁盘分区操作'), + # Format commands + (r'\bmkfs\b', 'Format filesystem'), + (r'\bfdisk\b', 'Disk partitioning operation'), + (r'\bparted\b', 'Disk partitioning operation'), - # 破坏性dd命令 - (r'\bdd\s+.*if=.*of=/dev/', '破坏性dd命令'), - (r'\bdd\s+.*if=/dev/zero', '使用/dev/zero的dd命令'), - (r'\bdd\s+.*if=/dev/urandom', '使用/dev/urandom的dd命令'), + # Destructive dd commands + (r'\bdd\s+.*if=.*of=/dev/', 'Destructive dd command'), + (r'\bdd\s+.*if=/dev/zero', 'dd command using /dev/zero'), + (r'\bdd\s+.*if=/dev/urandom', 'dd command using /dev/urandom'), - # 系统关键操作 - (r'\bchmod\s+.*777\s+.*/', '修改根目录权限'), - (r'\bchown\s+.*root\s+.*/', '修改根目录所有者'), + # Critical system operations + (r'\bchmod\s+.*777\s+.*/', 'Modify root directory permissions'), + (r'\bchown\s+.*root\s+.*/', 'Modify root directory ownership'), - # 其他危险操作 - (r'>\s*/dev/', '重定向到设备文件'), - (r':\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;', 'Fork炸弹'), - (r'\bhalt\b', '系统关机'), - (r'\bpoweroff\b', '系统关机'), - (r'\breboot\b', '系统重启'), - (r'\bshutdown\b', '系统关机'), + # Other dangerous operations + (r'>\s*/dev/', 'Redirect to device file'), + (r':\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;', 'Fork bomb'), + (r'\bhalt\b', 'System halt'), + (r'\bpoweroff\b', 'System poweroff'), + (r'\breboot\b', 'System reboot'), + (r'\bshutdown\b', 'System shutdown'), - # 删除所有文件的模式(但允许相对路径如 ./test) - (r'\brm\s+.*-.*rf\s+\*', '删除当前目录所有文件'), - (r'\brm\s+.*-.*rf\s+\.\.(?:\s|$|/)', '删除上级目录'), + # Patterns for removing all files (but allow relative paths like ./test) + (r'\brm\s+.*-.*rf\s+\*', 'Remove all files in current directory'), + (r'\brm\s+.*-.*rf\s+\.\.(?:\s|$|/)', 'Remove parent directory'), ] - # 允许的危险命令白名单(如果需要的话,可以在这里添加例外情况) + # Whitelist for allowed dangerous commands (add exceptions here if needed) ALLOWED_PATTERNS: List[str] = [ - # 可以在这里添加允许的模式 + # Add allowed patterns here ] @classmethod def validate(cls, command: str) -> None: """ - 验证命令是否安全。 + Validate if a command is safe to execute. Args: - command: 要执行的命令字符串 + command: Command string to execute Raises: - DangerousCommandError: 如果检测到危险命令 + DangerousCommandError: If a dangerous command is detected """ if not command or not command.strip(): return - # 标准化命令:去除多余空格,转换为小写进行匹配 + # Normalize command: remove extra spaces, convert to lowercase for matching normalized_command = ' '.join(command.split()) command_lower = normalized_command.lower() - # 检查是否匹配白名单 + # Check if matches whitelist for allowed_pattern in cls.ALLOWED_PATTERNS: if re.search(allowed_pattern, command_lower, re.IGNORECASE): return - # 检查是否匹配危险模式 + # Check if matches dangerous patterns for pattern, description in cls.DANGEROUS_PATTERNS: if re.search(pattern, command_lower, re.IGNORECASE): raise DangerousCommandError( - f"检测到危险命令: {description}\n" - f"命令: {command}\n" - f"模式: {pattern}" + f"Dangerous command detected: {description}\n" + f"Command: {command}\n" + f"Pattern: {pattern}" ) - # 额外检查:防止通过变量或引号绕过检查 - # 检查命令中是否包含明显的危险操作(使用单词边界确保精确匹配) + # Additional check: prevent bypassing through variables or quotes + # Check if command contains obvious dangerous operations (use word boundaries for exact matching) dangerous_keywords_patterns = [ (r'\brm\s+-rf\s+/\s*$', 'rm -rf /'), (r'\brm\s+-rf\s+/\s+', 'rm -rf /'), @@ -110,6 +110,6 @@ def validate(cls, command: str) -> None: for pattern, keyword_desc in dangerous_keywords_patterns: if re.search(pattern, command_lower, re.IGNORECASE): raise DangerousCommandError( - f"检测到危险命令关键词: {keyword_desc}\n" - f"命令: {command}" + f"Dangerous command keyword detected: {keyword_desc}\n" + f"Command: {command}" ) diff --git a/src/remoteshell_mcp/server.py b/src/remoteshell_mcp/server.py index 72dc1ce..15486d8 100644 --- a/src/remoteshell_mcp/server.py +++ b/src/remoteshell_mcp/server.py @@ -136,7 +136,7 @@ def execute_command( "error": str(e), "connection_id": connection_id, "command": command, - "message": f"命令验证失败: {e}" + "message": f"Command validation failed: {e}" } except (ValueError, SSHConnectionError, SSHCommandError) as e: return { From 84ccffc6eeef7548762edbfcb152af5b9e8183cb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 23 Dec 2025 11:33:39 +0000 Subject: [PATCH 3/5] feat: Add env var to bypass command validation Co-authored-by: zhouhua258 --- src/remoteshell_mcp/command_validator.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/remoteshell_mcp/command_validator.py b/src/remoteshell_mcp/command_validator.py index 43877a9..3b5ff0c 100644 --- a/src/remoteshell_mcp/command_validator.py +++ b/src/remoteshell_mcp/command_validator.py @@ -1,5 +1,6 @@ """Command validator to prevent execution of dangerous commands.""" +import os import re from typing import List, Tuple @@ -60,11 +61,6 @@ class CommandValidator: (r'\brm\s+.*-.*rf\s+\.\.(?:\s|$|/)', 'Remove parent directory'), ] - # Whitelist for allowed dangerous commands (add exceptions here if needed) - ALLOWED_PATTERNS: List[str] = [ - # Add allowed patterns here - ] - @classmethod def validate(cls, command: str) -> None: """ @@ -75,19 +71,22 @@ def validate(cls, command: str) -> None: Raises: DangerousCommandError: If a dangerous command is detected + + Note: + Validation can be bypassed by setting the REMOTESHELL_DANGEROUS + environment variable to any non-empty value. """ if not command or not command.strip(): return + # Skip validation if REMOTESHELL_DANGEROUS environment variable is set + if os.environ.get('REMOTESHELL_DANGEROUS'): + return + # Normalize command: remove extra spaces, convert to lowercase for matching normalized_command = ' '.join(command.split()) command_lower = normalized_command.lower() - # Check if matches whitelist - for allowed_pattern in cls.ALLOWED_PATTERNS: - if re.search(allowed_pattern, command_lower, re.IGNORECASE): - return - # Check if matches dangerous patterns for pattern, description in cls.DANGEROUS_PATTERNS: if re.search(pattern, command_lower, re.IGNORECASE): From fe278e56572b807d4eeeb98be990b6bfaff7f7ae Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 23 Dec 2025 11:35:41 +0000 Subject: [PATCH 4/5] Refactor: Rename dangerous env var to disable validation Co-authored-by: zhouhua258 --- src/remoteshell_mcp/command_validator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/remoteshell_mcp/command_validator.py b/src/remoteshell_mcp/command_validator.py index 3b5ff0c..90e0792 100644 --- a/src/remoteshell_mcp/command_validator.py +++ b/src/remoteshell_mcp/command_validator.py @@ -73,14 +73,14 @@ def validate(cls, command: str) -> None: DangerousCommandError: If a dangerous command is detected Note: - Validation can be bypassed by setting the REMOTESHELL_DANGEROUS + Validation can be bypassed by setting the REMOTESHELL_DISABLE_VALIDATION environment variable to any non-empty value. """ if not command or not command.strip(): return - # Skip validation if REMOTESHELL_DANGEROUS environment variable is set - if os.environ.get('REMOTESHELL_DANGEROUS'): + # Skip validation if REMOTESHELL_DISABLE_VALIDATION environment variable is set + if os.environ.get('REMOTESHELL_DISABLE_VALIDATION'): return # Normalize command: remove extra spaces, convert to lowercase for matching From fbfeadd52e770f4465c50d3b30c247049d4b6d14 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 23 Dec 2025 11:36:18 +0000 Subject: [PATCH 5/5] feat: Add command validation to prevent dangerous operations Co-authored-by: zhouhua258 --- README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e28710..635658d 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,13 @@ Execute a command on a remote host. - `timeout` (optional): Command timeout in seconds - `working_dir` (optional): Working directory for command execution +**Command Validation:** +The server includes built-in protection against dangerous commands (e.g., `rm -rf /`, `mkfs`, `dd` operations on devices). Dangerous commands will be blocked by default. To disable validation, set the `REMOTESHELL_DISABLE_VALIDATION` environment variable: + +```bash +export REMOTESHELL_DISABLE_VALIDATION=1 +``` + **Example Usage:** ``` Execute "ls -la /home" on prod-server @@ -299,11 +306,24 @@ Upload all .conf files from /etc/local to /etc/remote on backup-server - Keys can be password-protected for additional security - Use different keys for different hosts when possible -3. **Connection Timeouts**: +3. **Command Validation**: + - The server includes built-in protection against dangerous commands + - Commands that could damage the system are blocked by default, including: + - Deleting root or critical system directories (`rm -rf /`, `rm -rf /etc`, etc.) + - Formatting filesystems (`mkfs`, `fdisk`, `parted`) + - Destructive `dd` operations + - System shutdown/reboot commands (`halt`, `poweroff`, `reboot`, `shutdown`) + - To disable command validation (not recommended), set the `REMOTESHELL_DISABLE_VALIDATION` environment variable: + ```bash + export REMOTESHELL_DISABLE_VALIDATION=1 + ``` + - **Warning**: Disabling validation removes an important safety layer. Only do this if you fully trust the commands being executed. + +4. **Connection Timeouts**: - Set appropriate timeouts to prevent hanging connections - Connections automatically reconnect if they drop -4. **Global Config File**: +5. **Global Config File**: - Located at `~/.remoteShell/config.json` - Should have restrictive permissions (600) - Consider encrypting sensitive data at rest