{"record":{"id":"07a7f1e1a884c32b","repo":"abhigyanpatwari/GitNexus","slug":"candidate-overlay-cannot-traverse-symlinks-overl","errorCode":null,"errorMessage":"candidate overlay cannot traverse symlinks: {overlay}","messagePattern":"candidate overlay cannot traverse symlinks: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"eval/workflow_bench/evolution.py","lineNumber":279,"sourceCode":"        env=build_sandbox_environment(),\n    )\n    return command, result\n\n\ndef candidate_overlay_files(overlay: Path) -> list[Path]:\n    \"\"\"Return a candidate's files after enforcing the benchmark trust boundary.\n\n    Candidates may change only the canonical repo-local skill prompts. They\n    cannot modify task code, tests, or verification commands and thereby game\n    the promotion gate.\n    \"\"\"\n    overlay = overlay.expanduser().absolute()\n    try:\n        resolved_overlay = overlay.resolve(strict=True)\n    except OSError as exc:\n        raise ValueError(f\"candidate overlay is not a directory: {overlay}\") from exc\n    if resolved_overlay != overlay:\n        raise ValueError(f\"candidate overlay cannot traverse symlinks: {overlay}\")\n    _require_real_directory(overlay, label=\"candidate overlay\")\n\n    entries: list[Path] = []\n    pending = [overlay]\n    entry_count = 0\n    while pending:\n        directory = pending.pop()\n        child_directories: list[Path] = []\n        try:\n            iterator = os.scandir(directory)\n        except OSError as exc:\n            raise ValueError(f\"candidate overlay directory is unreadable: {directory}: {exc}\") from exc\n        with iterator:\n            for item in iterator:\n                entry_count += 1\n                if entry_count > MAX_CANDIDATE_ENTRIES:\n                    raise ValueError(f\"candidate overlay exceeds the {MAX_CANDIDATE_ENTRIES}-entry limit\")\n                path = Path(item.path)","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/evolution.py#L261-L297","documentation":"Thrown by candidate_overlay_files (evolution.py:279) when the resolved overlay path differs from the lexical absolute path. The harness requires the overlay root to be a real path that does not traverse any symlink, because resolve() following a symlink would hide a redirect outside the trusted tree. Any symlink component in the supplied path triggers this.","triggerScenarios":"The overlay path contains a symlink component (the root itself is a symlink, or a parent like /tmp is a symlink). resolve() lands on a different path than the lexical absolute input, so the equality check fails.","commonSituations":"On macOS /tmp -> /private/tmp and /var -> /private/var; pointing at a symlinked project checkout; a CI workspace that symlinks into another volume.","solutions":["Pass a real (non-symlink) absolute path; resolve the symlink yourself and pass the resolved real path if it is itself real.","Copy the overlay into a fresh real directory (shutil.copytree into tempfile.mkdtemp) and pass that.","On macOS use /private/tmp/... directly instead of /tmp/...; avoid symlinked project roots."],"exampleFix":"# before: overlay root is a symlink (e.g. macOS /tmp)\napply_candidate_overlay(Path('/tmp/overlay'), ...)  # /tmp -> /private/tmp\n\n# after: copy to a real path with no symlink components\nimport tempfile, shutil\nreal = Path(tempfile.mkdtemp(prefix='wfbench-overlay-'))\nshutil.copytree('/tmp/overlay', real / 'overlay')\nassert (real / 'overlay').resolve() == (real / 'overlay')  # no symlink hop\napply_candidate_overlay(real / 'overlay', ...)","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef overlay_has_no_symlink_hop(p: Path) -> bool:\n    p = p.expanduser().absolute()\n    try:\n        return p.resolve(strict=True) == p\n    except OSError:\n        return False","typeGuard":"null","tryCatchPattern":"try:\n    apply_candidate_overlay(overlay, worktree, sandbox=sandbox)\nexcept ValueError as exc:\n    if 'cannot traverse symlinks' in str(exc):\n        # copy overlay to a real path (no symlink components) and retry\n        ...","preventionTips":["Pass a real absolute path with no symlink components.","On macOS use /private/tmp not /tmp; avoid symlinked project roots.","Snapshot the overlay into tempfile.mkdtemp() before benchmarking."],"tags":["symlink","security","overlay","filesystem","trust-boundary"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}