T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/gate.py:333- Finding
Artifact-Controlled Rollback Allows Arbitrary File Replacement
- Content
View full analysis
Vulnerability Details
File Location:
scripts/gate.py:333-348
Vulnerability Type: Unrestricted filesystem path use during backup restoration
Risk Level: HighVulnerable Code
python def maybe_restore(execution_artifact: dict) -> dict: result = execution_artifact.get("result", {}) rollback = result.get("rollback_pointer") if not rollback or not result.get("modified"): return {"attempted": False, "success": False, "reason": "no modified file to restore"} backup_path = Path(rollback["backup_path"]).expanduser().resolve() target_path = Path(rollback["target_path"]).expanduser().resolve() restore_backup(backup_path, target_path) return { "attempted": True, "success": True, "backup_path": str(backup_path), "target_path": str(target_path), }Rollback is automatically invoked from several decision paths, including required gate failures:
python if not layer_verdict["all_passed"]: rollback_outcome = {"attempted": False, "success": False, "reason": "not_needed"} if execution_result.get("modified"): decision = "revert" rollback_outcome = maybe_restore(execution_artifact)Technical Analysis
The
backup_pathandtarget_pathvalues are obtained directly from the supplied execution artifact. Callingresolve()canonicalizes a path but does not establish that it is authorized.The implementation does not verify that:
- The target is inside an approved project or workspace directory.
- The target matches the candidate's declared
target_path. - The backup was created by the trusted executor for the current run.
- The target or its parent directories do not traverse symbolic links.
- The backup is a regular file rather than an unexpected filesystem object.
Consequently, an attacker who can provide or alter an execution artifact can choose both the source and destination of the restoration operation. When the process runs with ...[truncated 1529 chars]
- Remediation
View remediation
Remediation Suggestions
- Configure an explicit, trusted workspace root and require both rollback paths to remain within approved directories.
- Use canonical containment checks after resolving paths:
python workspace = configured_workspace.resolve() target = Path(rollback["target_path"]).resolve(strict=False) if target != workspace and workspace not in target.parents: raise ValueError("Rollback target is outside the approved workspace")- Require the canonical rollback target to exactly match the canonical target recorded in the trusted candidate or executor state.
- Do not trust a backup path supplied solely through the execution artifact. Record backups in a trusted server-side registry and reference them using an opaque identifier.
- Require backup files to reside in a dedicated backup directory with restrictive permissions.
- Reject symbolic links and verify relevant filesystem objects using
lstat()immediately before restoration. - Restore files atomically using a securely created temporary file in the destination directory, followed by an atomic replacement.
- Run the gate under a least-privileged account that cannot write system, credential, or deployment-control files.
- Add negative tests for absolute paths,
..traversal, symlink escapes, mismatched candidate targets, and unregistered backup files.
