# SECURITY FIXES CHECKLIST ## 20+ Specific Security Fixes Required This document provides a detailed checklist of all security fixes identified in the comprehensive audit. --- ## CRITICAL FIXES (Must implement immediately) ### Fix 1: Remove shell=True from subprocess calls **File:** `tools/terminal_tool.py` **Line:** ~457 **CVSS:** 9.8 ```python # BEFORE subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ...) # AFTER # Validate command first if not is_safe_command(exec_command): raise SecurityError("Dangerous command detected") subprocess.Popen(cmd_list, shell=False, ...) # Pass as list ``` --- ### Fix 2: Implement path sandbox validation **File:** `tools/file_operations.py` **Lines:** 409-420 **CVSS:** 9.1 ```python # BEFORE def _expand_path(self, path: str) -> str: if path.startswith('~'): return os.path.expanduser(path) return path # AFTER def _expand_path(self, path: str) -> Path: safe_root = Path(self.cwd).resolve() expanded = Path(path).expanduser().resolve() if not str(expanded).startswith(str(safe_root)): raise PermissionError(f"Path {path} outside allowed directory") return expanded ``` --- ### Fix 3: Environment variable sanitization **File:** `tools/code_execution_tool.py` **Lines:** 434-461 **CVSS:** 9.3 ```python # BEFORE _SAFE_ENV_PREFIXES = ("PATH", "HOME", "USER", ...) _SECRET_SUBSTRINGS = ("TOKEN", "SECRET", ...) # AFTER _ALLOWED_ENV_VARS = frozenset([ "PATH", "HOME", "USER", "LANG", "LC_ALL", "TERM", "SHELL", "PWD", "PYTHONPATH" ]) child_env = {k: v for k, v in os.environ.items() if k in _ALLOWED_ENV_VARS} ``` --- ### Fix 4: Secure sudo password handling **File:** `tools/terminal_tool.py` **Line:** 275 **CVSS:** 9.0 ```python # BEFORE exec_command = f"printf '%s\\n' {shlex.quote(sudo_stdin.rstrip())} | {exec_command}" # AFTER # Use file descriptor passing instead of command line with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: f.write(sudo_stdin) pass_file = f.name os.chmod(pass_file, 0o600) exec_command = f"cat {pass_file} | {exec_command}" # Clean up after execution ``` --- ### Fix 5: Connection-level URL validation **File:** `tools/url_safety.py` **Lines:** 50-96 **CVSS:** 9.4 ```python # AFTER - Add to is_safe_url() # After DNS resolution, verify IP is not in private range def _validate_connection_ip(hostname: str) -> bool: try: addr = socket.getaddrinfo(hostname, None) for a in addr: ip = ipaddress.ip_address(a[4][0]) if ip.is_private or ip.is_loopback or ip.is_reserved: return False return True except: return False ``` --- ## HIGH PRIORITY FIXES ### Fix 6: MCP OAuth token validation **File:** `tools/mcp_oauth.py` **Lines:** 66-89 **CVSS:** 8.8 ```python # AFTER async def get_tokens(self): data = self._read_json(self._tokens_path()) if not data: return None # Add schema validation if not self._validate_token_schema(data): logger.error("Invalid token schema, deleting corrupted tokens") self.remove() return None return OAuthToken(**data) ``` --- ### Fix 7: API Server SQL injection prevention **File:** `gateway/platforms/api_server.py` **Lines:** 98-126 **CVSS:** 8.5 ```python # AFTER import uuid def _validate_response_id(self, response_id: str) -> bool: """Validate response_id format to prevent injection.""" try: uuid.UUID(response_id.split('-')[0], version=4) return True except (ValueError, IndexError): return False ``` --- ### Fix 8: CORS strict validation **File:** `gateway/platforms/api_server.py` **Lines:** 324-328 **CVSS:** 8.2 ```python # AFTER if "*" in self._cors_origins: logger.error("Wildcard CORS not allowed with credentials") return None # Reject wildcard with credentials ``` --- ### Fix 9: Require explicit API key **File:** `gateway/platforms/api_server.py` **Lines:** 360-361 **CVSS:** 8.1 ```python # AFTER if not self._api_key: logger.error("API server started without authentication") return web.json_response( {"error": "Server authentication not configured"}, status=500 ) ``` --- ### Fix 10: CDP URL validation **File:** `tools/browser_tool.py` **Lines:** 195-208 **CVSS:** 8.4 ```python # AFTER def _resolve_cdp_override(self, cdp_url: str) -> str: parsed = urlparse(cdp_url) if parsed.scheme not in ('ws', 'wss', 'http', 'https'): raise ValueError("Invalid CDP scheme") if parsed.hostname not in self._allowed_cdp_hosts: raise ValueError("CDP host not in allowlist") return cdp_url ``` --- ### Fix 11: Skills guard normalization **File:** `tools/skills_guard.py` **Lines:** 82-484 **CVSS:** 7.8 ```python # AFTER - Add to scan_skill() def normalize_for_scanning(content: str) -> str: """Normalize content to detect obfuscated threats.""" # Normalize Unicode content = unicodedata.normalize('NFKC', content) # Normalize case content = content.lower() # Remove common obfuscation content = content.replace('\\x', '') content = content.replace('\\u', '') return content ``` --- ### Fix 12: Docker volume validation **File:** `tools/environments/docker.py` **Line:** 267 **CVSS:** 8.7 ```python # AFTER _BLOCKED_PATHS = ['/var/run/docker.sock', '/proc', '/sys', '/dev'] for vol in volumes: if any(blocked in vol for blocked in _BLOCKED_PATHS): raise SecurityError(f"Volume mount {vol} blocked") volume_args.extend(["-v", vol]) ``` --- ### Fix 13: Secure error messages **File:** Multiple files **CVSS:** 7.5 ```python # AFTER - Add to all exception handlers try: operation() except Exception as e: logger.error(f"Error: {e}", exc_info=True) # Full details for logs raise UserError("Operation failed") # Generic for user ``` --- ### Fix 14: OAuth state validation **File:** `tools/mcp_oauth.py` **Line:** 186 **CVSS:** 7.6 ```python # AFTER code, state = await _wait_for_callback() stored_state = storage.get_state() if not hmac.compare_digest(state, stored_state): raise SecurityError("OAuth state mismatch - possible CSRF") ``` --- ### Fix 15: File operation race condition fix **File:** `tools/file_operations.py` **CVSS:** 7.4 ```python # AFTER import fcntl def safe_file_access(path: Path): fd = os.open(path, os.O_RDONLY) try: fcntl.flock(fd, fcntl.LOCK_SH) # Perform operations on fd, not path return os.read(fd, size) finally: fcntl.flock(fd, fcntl.LOCK_UN) os.close(fd) ``` --- ### Fix 16: Add rate limiting **File:** `gateway/platforms/api_server.py` **CVSS:** 7.3 ```python # AFTER - Add middleware from aiohttp_limiter import Limiter limiter = Limiter( rate=100, # requests per=60, # per minute key_func=lambda req: req.remote ) @app.middleware async def rate_limit_middleware(request, handler): if not limiter.is_allowed(request): return web.json_response( {"error": "Rate limit exceeded"}, status=429 ) return await handler(request) ``` --- ### Fix 17: Secure temp file creation **File:** `tools/code_execution_tool.py` **Line:** 388 **CVSS:** 7.2 ```python # AFTER import tempfile import os fd, tmpdir = tempfile.mkstemp(prefix="hermes_sandbox_", suffix=".tmp") os.chmod(tmpdir, 0o700) # Owner only os.close(fd) # Use tmpdir securely ``` --- ## MEDIUM PRIORITY FIXES ### Fix 18: Expand dangerous patterns **File:** `tools/approval.py` **Lines:** 40-78 **CVSS:** 6.5 Add patterns: ```python (r'\bcurl\s+.*\|\s*sh\b', "pipe remote content to shell"), (r'\bwget\s+.*\|\s*bash\b', "pipe remote content to shell"), (r'python\s+-c\s+.*import\s+os', "python os import"), (r'perl\s+-e\s+.*system', "perl system call"), ``` --- ### Fix 19: Credential file permissions **File:** `tools/credential_files.py`, `tools/mcp_oauth.py` **CVSS:** 6.4 ```python # AFTER def _write_json(path: Path, data: dict) -> None: path.write_text(json.dumps(data, indent=2), encoding="utf-8") path.chmod(0o600) # Verify permissions were set stat = path.stat() if stat.st_mode & 0o077: raise SecurityError("Failed to set restrictive permissions") ``` --- ### Fix 20: Log sanitization **File:** Multiple logging statements **CVSS:** 5.8 ```python # AFTER from agent.redact import redact_sensitive_text # In all logging calls logger.info(redact_sensitive_text(f"Processing {user_input}")) ``` --- ## ADDITIONAL FIXES (21-32) ### Fix 21: XXE Prevention **File:** PowerPoint XML processing Add: ```python from defusedxml import ElementTree as ET # Use defusedxml instead of standard xml ``` --- ### Fix 22: YAML Safe Loading Audit **File:** `hermes_cli/config.py` Audit all yaml.safe_load calls for custom constructors. --- ### Fix 23: Prototype Pollution Fix **File:** `scripts/whatsapp-bridge/bridge.js` Use Map instead of Object for user-controlled keys. --- ### Fix 24: Subagent Isolation **File:** `tools/delegate_tool.py` Implement filesystem namespace isolation. --- ### Fix 25: Secure Session IDs **File:** `gateway/session.py` Use secrets.token_urlsafe(32) instead of uuid4. --- ### Fix 26: Binary Integrity Checks **File:** `tools/tirith_security.py` Require GPG signature verification. --- ### Fix 27: Debug Output Redaction **File:** `tools/debug_helpers.py` Apply redact_sensitive_text to all debug output. --- ### Fix 28: Security Headers **File:** `gateway/platforms/api_server.py` Add: ```python "Content-Security-Policy": "default-src 'self'", "Strict-Transport-Security": "max-age=31536000", ``` --- ### Fix 29: Version Information Minimization **File:** Version endpoints Return minimal version information publicly. --- ### Fix 30: Dead Code Removal **File:** Multiple Remove unused imports and functions. --- ### Fix 31: Token Encryption at Rest **File:** `hermes_cli/auth.py` Use OS keychain or encrypt auth.json. --- ### Fix 32: Input Length Validation **File:** All tool entry points Add MAX_INPUT_LENGTH checks everywhere. --- ## IMPLEMENTATION VERIFICATION ### Testing Requirements - [ ] All fixes have unit tests - [ ] Security regression tests pass - [ ] Fuzzing shows no new vulnerabilities - [ ] Penetration test completed - [ ] Code review by security team ### Sign-off Required - [ ] Security Team Lead - [ ] Engineering Manager - [ ] QA Lead - [ ] DevOps Lead --- **Last Updated:** March 30, 2026 **Next Review:** After all P0/P1 fixes completed