Files
hermes-agent/SECURITY_FIXES_CHECKLIST.md
Allegro 10271c6b44
Some checks failed
Supply Chain Audit / Scan PR for supply chain risks (pull_request) Failing after 25s
Tests / test (pull_request) Failing after 24s
Docker Build and Publish / build-and-push (pull_request) Failing after 35s
security: fix command injection vulnerabilities (CVSS 9.8)
Replace shell=True with list-based subprocess execution to prevent
command injection via malicious user input.

Changes:
- tools/transcription_tools.py: Use shlex.split() + shell=False
- tools/environments/docker.py: List-based commands with container ID validation

Fixes CVE-level vulnerability where malicious file paths or container IDs
could inject arbitrary commands.

CVSS: 9.8 (Critical)
Refs: V-001 in SECURITY_AUDIT_REPORT.md
2026-03-30 23:15:11 +00:00

10 KiB

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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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:

(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

# 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

# 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:

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:

"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