{"record":{"id":"0c9e46a3b9021127","repo":"abhigyanpatwari/GitNexus","slug":"candidate-destination-escapes-the-clone-relative","errorCode":null,"errorMessage":"candidate destination escapes the clone: {relative}","messagePattern":"candidate destination escapes the clone: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"eval/workflow_bench/evolution.py","lineNumber":170,"sourceCode":"    return _fingerprint_payload(payload), payload\n\n\ndef _fingerprint_payload(payload: list[tuple[PurePosixPath, bytes]]) -> str:\n    digest = hashlib.sha256()\n    for relative_path, content in payload:\n        relative = relative_path.as_posix().encode()\n        digest.update(len(relative).to_bytes(8, \"big\"))\n        digest.update(relative)\n        digest.update(len(content).to_bytes(8, \"big\"))\n        digest.update(content)\n    return digest.hexdigest()\n\n\ndef _replace_regular_file(root: Path, relative: Path, content: bytes) -> None:\n    \"\"\"Replace a clone file through validated directory descriptors.\"\"\"\n\n    if relative.is_absolute() or not relative.parts or \"..\" in relative.parts:\n        raise ValueError(f\"candidate destination escapes the clone: {relative}\")\n    _require_real_directory(root, label=\"candidate destination root\")\n    directory_flags = os.O_RDONLY | getattr(os, \"O_DIRECTORY\", 0) | getattr(os, \"O_NOFOLLOW\", 0)\n    descriptor = os.open(root, directory_flags)\n    try:\n        for part in relative.parts[:-1]:\n            try:\n                os.mkdir(part, mode=0o700, dir_fd=descriptor)\n            except FileExistsError:\n                pass\n            try:\n                child = os.open(part, directory_flags, dir_fd=descriptor)\n            except OSError as exc:\n                raise ValueError(\n                    f\"candidate destination parent must be a real directory: {relative.parent}: {exc}\"\n                ) from exc\n            os.close(descriptor)\n            descriptor = child\n","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/evolution.py#L152-L188","documentation":"Thrown by _replace_regular_file (evolution.py:170) before writing an overlay file into the clone. It rejects any relative path that is_absolute(), has no parts, or contains a '..' component. This is the path-traversal guard preventing a candidate overlay from writing outside the clone root through a relative destination.","triggerScenarios":"An overlay file whose repo-relative path includes '..' (e.g. .claude/skills/../../etc/passwd), an absolute path (/etc/x), or an empty leaf name. Any such destination would escape the clone and is refused before any directory-fd walk begins.","commonSituations":"Manually crafting an overlay that reuses an existing repo path with a '../' shortcut; a buggy script that builds overlay paths from user input without normalization; an adversarial candidate attempting to escape the trust boundary.","solutions":["Remove all '..' and absolute components from overlay paths; keep paths as clean relative POSIX paths under .claude/skills/.","Sanitize with PurePosixPath: reject p.is_absolute() or '..' in p.parts, then pass only the normalized result.","Re-create the overlay from a clean checkout so no stray traversal components remain."],"exampleFix":"# before: overlay contains a traversal path\n.claude/skills/../../package.json\n\n# after: keep only real skill paths; guard before applying\nfrom pathlib import PurePosixPath\nrel = PurePosixPath('.claude/skills/gitnexus-work/SKILL.md')\nassert not rel.is_absolute() and rel.parts and '..' not in rel.parts, 'escapes clone'","handlingStrategy":"validation","validationCode":"from pathlib import PurePosixPath\n\ndef overlay_path_is_safe(relative: PurePosixPath) -> bool:\n    return (\n        not relative.is_absolute()\n        and bool(relative.parts)\n        and '..' not in relative.parts\n        and '' not in relative.parts\n    )","typeGuard":"from pathlib import PurePosixPath\n\ndef is_safe_overlay_path(p: PurePosixPath) -> bool:\n    return not p.is_absolute() and bool(p.parts) and '..' not in p.parts and '' not in p.parts","tryCatchPattern":"try:\n    apply_candidate_overlay(overlay, worktree, sandbox=sandbox)\nexcept ValueError as exc:\n    if 'escapes the clone' in str(exc):\n        raise SystemExit(f'refuse: overlay path traversal detected: {exc}')","preventionTips":["Build overlay paths only from trusted constants, never raw user input.","Reject '..' and absolute components before constructing the overlay tree.","Treat this error as a potential integrity attack, not a recoverable typo."],"tags":["path-traversal","security","overlay","trust-boundary"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}