This commit is contained in:
65
src/lai_automation/policy.py
Normal file
65
src/lai_automation/policy.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path, PurePosixPath
|
||||
|
||||
from .config import Settings
|
||||
from .git import ChangeSummary
|
||||
|
||||
_ALWAYS_DENIED = (
|
||||
".env",
|
||||
"vendor/",
|
||||
"storage/",
|
||||
"bootstrap/cache/",
|
||||
"docker/",
|
||||
"docker-compose",
|
||||
".gitea/",
|
||||
".github/",
|
||||
)
|
||||
|
||||
_DIRECT_MAIN_DENIED = (
|
||||
"database/migrations/",
|
||||
"app/Http/Middleware/",
|
||||
"app/Models/User.php",
|
||||
"app/Services/PermissionService.php",
|
||||
"app/Services/SQLValidatorService.php",
|
||||
"config/auth.php",
|
||||
"config/database.php",
|
||||
"routes/",
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PolicyResult:
|
||||
allowed: bool
|
||||
errors: tuple[str, ...]
|
||||
|
||||
|
||||
def evaluate_changes(
|
||||
summary: ChangeSummary,
|
||||
settings: Settings,
|
||||
workspace: Path | None = None,
|
||||
) -> PolicyResult:
|
||||
errors: list[str] = []
|
||||
if not summary.files:
|
||||
errors.append("Agent produced no file changes")
|
||||
if len(summary.files) > settings.max_changed_files:
|
||||
errors.append(f"Changed {len(summary.files)} files; limit is {settings.max_changed_files}")
|
||||
if summary.changed_lines > settings.max_changed_lines:
|
||||
errors.append(f"Changed {summary.changed_lines} lines; limit is {settings.max_changed_lines}")
|
||||
|
||||
for filename in summary.files:
|
||||
if PurePosixPath(filename).is_absolute() or ".." in PurePosixPath(filename).parts:
|
||||
errors.append(f"Invalid changed path: {filename}")
|
||||
continue
|
||||
if workspace and (workspace / filename).is_symlink():
|
||||
errors.append(f"Symbolic links are not allowed: {filename}")
|
||||
continue
|
||||
if any(filename == prefix or filename.startswith(prefix) for prefix in _ALWAYS_DENIED):
|
||||
errors.append(f"Denied path changed: {filename}")
|
||||
if settings.mode == "direct-main" and any(
|
||||
filename == prefix or filename.startswith(prefix) for prefix in _DIRECT_MAIN_DENIED
|
||||
):
|
||||
errors.append(f"Direct-main denied path changed: {filename}")
|
||||
|
||||
return PolicyResult(not errors, tuple(errors))
|
||||
Reference in New Issue
Block a user