security: validate pkill process names against safe character set

Address CodeRabbit security feedback - restrict pkill_processes entries
to alphanumeric names with dots, underscores, and hyphens only.

This prevents potential exploitation through regex metacharacters like
'.*' being registered as process names.

Changes:
- Added VALID_PROCESS_NAME_PATTERN regex constant
- Updated both org and project config validation to:
  - Normalize (trim whitespace) process names
  - Reject names with regex metacharacters
  - Reject names with spaces
- Added 3 new tests for regex validation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cabana8471
2026-01-25 16:34:56 +01:00
parent dbbc7d5ce5
commit fed2516f08
2 changed files with 90 additions and 2 deletions

View File

@@ -7,12 +7,17 @@ Uses an allowlist approach - only explicitly permitted commands can run.
"""
import os
import re
import shlex
from pathlib import Path
from typing import Optional
import yaml
# Regex pattern for valid pkill process names (no regex metacharacters allowed)
# Matches alphanumeric names with dots, underscores, and hyphens
VALID_PROCESS_NAME_PATTERN = re.compile(r"^[A-Za-z0-9._-]+$")
# Allowed commands for development tasks
# Minimal set needed for the autonomous coding demo
ALLOWED_COMMANDS = {
@@ -474,9 +479,17 @@ def load_org_config() -> Optional[dict]:
processes = config["pkill_processes"]
if not isinstance(processes, list):
return None
# Normalize and validate each process name against safe pattern
normalized = []
for proc in processes:
if not isinstance(proc, str) or proc.strip() == "":
if not isinstance(proc, str):
return None
proc = proc.strip()
# Block empty strings and regex metacharacters
if not proc or not VALID_PROCESS_NAME_PATTERN.fullmatch(proc):
return None
normalized.append(proc)
config["pkill_processes"] = normalized
return config
@@ -536,9 +549,17 @@ def load_project_commands(project_dir: Path) -> Optional[dict]:
processes = config["pkill_processes"]
if not isinstance(processes, list):
return None
# Normalize and validate each process name against safe pattern
normalized = []
for proc in processes:
if not isinstance(proc, str) or proc.strip() == "":
if not isinstance(proc, str):
return None
proc = proc.strip()
# Block empty strings and regex metacharacters
if not proc or not VALID_PROCESS_NAME_PATTERN.fullmatch(proc):
return None
normalized.append(proc)
config["pkill_processes"] = normalized
return config

View File

@@ -809,6 +809,73 @@ pkill_processes:
print(f" FAIL: pkill python should be allowed with org config: {result}")
failed += 1
# Test 9: Regex metacharacters should be rejected in pkill_processes
with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder"
org_dir.mkdir()
org_config_path = org_dir / "config.yaml"
# Try to register a regex pattern (should be rejected)
org_config_path.write_text("""version: 1
pkill_processes:
- ".*"
""")
config = load_org_config()
if config is None:
print(" PASS: Regex pattern '.*' rejected in pkill_processes")
passed += 1
else:
print(" FAIL: Regex pattern '.*' should be rejected")
failed += 1
# Test 10: Valid process names with dots/underscores/hyphens should be accepted
with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder"
org_dir.mkdir()
org_config_path = org_dir / "config.yaml"
# Valid names with special chars
org_config_path.write_text("""version: 1
pkill_processes:
- my-app
- app_server
- node.js
""")
config = load_org_config()
if config is not None and config.get("pkill_processes") == ["my-app", "app_server", "node.js"]:
print(" PASS: Valid process names with dots/underscores/hyphens accepted")
passed += 1
else:
print(f" FAIL: Valid process names should be accepted: {config}")
failed += 1
# Test 11: Names with spaces should be rejected
with tempfile.TemporaryDirectory() as tmphome:
with tempfile.TemporaryDirectory() as tmpproject:
with temporary_home(tmphome):
org_dir = Path(tmphome) / ".autocoder"
org_dir.mkdir()
org_config_path = org_dir / "config.yaml"
org_config_path.write_text("""version: 1
pkill_processes:
- "my app"
""")
config = load_org_config()
if config is None:
print(" PASS: Process name with space rejected")
passed += 1
else:
print(" FAIL: Process name with space should be rejected")
failed += 1
return passed, failed