T09 · Insecure Skill Coding Practices
- Location
scripts/check_browser_proof_bundle.py:30- Finding
Repository Boundary Bypass Through Artifact Path Traversal
- Content
View full analysis
Vulnerability Details
File Location:
scripts/check_browser_proof_bundle.py, lines 30–31 and 84–90
Vulnerability Type: Path traversal and filesystem existence disclosure
Risk Level: MediumVulnerable Code:
python def is_absolute_or_private(path_text: str) -> bool: return path_text.startswith("/") or path_text.startswith("~") or ":\\" in path_text or path_text.startswith("file://")python for field in ARTIFACT_FIELDS: artifact_path = artifacts.get(field, "").strip() if not artifact_path: continue if is_absolute_or_private(artifact_path): add_finding(findings, "error", "absolute-artifact-path", f"{field} uses an absolute or private path.", step_id) continue candidate = repo_root / artifact_path if not candidate.exists(): add_finding(findings, "warning", "missing-artifact", f"{field} path does not exist under repo root: {artifact_path}", step_id)Technical Analysis
The validator rejects several forms of absolute path but does not reject parent-directory components such as
... It also does not resolve the constructed path and verify that the result remains beneath the resolved repository root.Consequently, an artifact value such as
../../etc/passwdis treated as a relative path and joined torepo_root. Filesystem path resolution can then escape the intended repository boundary. The code only callsPath.exists()and does not read or return file contents, so this is not arbitrary file disclosure. It nevertheless creates a local filesystem existence oracle and can incorrectly accept an artifact located outside the evidence repository as valid.Symbolic links inside the repository can cause a similar boundary escape unless containment is checked after canonical resolution.
Attack Path
- An attacker supplies or modifies a browser-proof manifest.
- The attacker sets an artifact field such as `screenshot ...[truncated 1077 chars]
- Remediation
View remediation
Remediation Suggestions
Resolve both paths and enforce containment before performing any filesystem operation:
python repo_root = Path(args.repo_root).expanduser().resolve() candidate = (repo_root / artifact_path).resolve() try: candidate.relative_to(repo_root) except ValueError: add_finding( findings, "error", "artifact-path-traversal", f"{field} escapes the repository root.", step_id, ) continue if not candidate.exists(): add_finding( findings, "warning", "missing-artifact", f"{field} path does not exist under repo root: {artifact_path}", step_id, )Additionally:
- Explicitly reject
..path components as an early validation measure. - Perform containment checks on resolved paths to address symbolic-link escapes.
- Require artifacts to be regular files where appropriate.
- Add tests for traversal paths, symbolic links, absolute paths, Windows path forms, and valid nested repository paths.
- Explicitly reject
