Files
lai_automatisierung/src/lai_automation/policy.py
Christian Meyer c2028fa256
Some checks failed
CI / test (push) Has been cancelled
CI / test (pull_request) Has been cancelled
fix: tighten agent patch hygiene
2026-08-31 03:02:42 +02:00

68 lines
2.5 KiB
Python

from __future__ import annotations
from pathlib import PurePosixPath
from .config import Settings
from .git import GitWorkspace
from .models import ChangeSummary
_PROTECTED_PARTS = {".git", "vendor", "node_modules", "storage"}
_PROTECTED_PREFIXES = (
".gitea/",
"bootstrap/cache/",
"docker/",
)
_PROTECTED_FILES = {
"Dockerfile",
"docker-compose.yml",
"docker-compose.yaml",
"compose.yml",
"compose.yaml",
}
_CODE_SUFFIXES = {".php", ".js", ".ts", ".vue", ".css", ".py"}
_TEMPORARY_SUFFIXES = {".bak", ".backup", ".orig", ".rej", ".swp", ".temp", ".tmp"}
_TEMPORARY_NAMES = {"debug.php", "test_debug.php", "scratch.php"}
def validate_changes(
workspace: GitWorkspace,
summary: ChangeSummary,
settings: Settings,
) -> None:
if workspace.has_path(".git"):
raise ValueError("The agent created forbidden Git metadata in the worktree")
if not summary.files:
raise ValueError("The agent did not produce a change")
if len(summary.files) > settings.max_changed_files:
raise ValueError(
f"Changed file limit exceeded "
f"({len(summary.files)} > {settings.max_changed_files})"
)
if summary.changed_lines > settings.max_changed_lines:
raise ValueError(
f"Changed line limit exceeded "
f"({summary.changed_lines} > {settings.max_changed_lines})"
)
code_changed = False
for raw_name in summary.files:
name = raw_name.replace("\\", "/")
path = PurePosixPath(name)
if name.startswith("/") or ".." in path.parts:
raise ValueError(f"Unsafe changed path: {name}")
if name.startswith(".env") or path.name.startswith(".env"):
raise ValueError(f"Protected environment file changed: {name}")
if path.parts and path.parts[0] in _PROTECTED_PARTS:
raise ValueError(f"Protected path changed: {name}")
if name in _PROTECTED_FILES or name.startswith(_PROTECTED_PREFIXES):
raise ValueError(f"Protected deployment path changed: {name}")
if path.suffix.lower() in _TEMPORARY_SUFFIXES or path.name.lower() in _TEMPORARY_NAMES:
raise ValueError(f"Temporary artifact changed: {name}")
if workspace.has_symlink(name):
raise ValueError(f"Symbolic links are not allowed in agent changes: {name}")
if path.suffix in _CODE_SUFFIXES and not name.startswith(("tests/", "docs/")):
code_changed = True
if not code_changed:
raise ValueError("The agent must change at least one application source file")