{"record":{"id":"decccb29da8c7fc5","repo":"abhigyanpatwari/GitNexus","slug":"target-gitnexus-path-must-be-a-real-directory-bef","errorCode":null,"errorMessage":"target .gitnexus path must be a real directory before graph preparation","messagePattern":"target \\.gitnexus path must be a real directory before graph preparation","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"critical","filePath":"eval/workflow_bench/sanitized_graph.py","lineNumber":141,"sourceCode":"        while view:\n            written = os.write(descriptor, view)\n            if written <= 0:\n                raise OSError(f\"short write while neutralizing {name}\")\n            view = view[written:]\n        os.fsync(descriptor)\n    finally:\n        os.close(descriptor)\n\n\ndef _neutralize_target_index_inputs(root: Path) -> None:\n    index = root / \".gitnexus\"\n    try:\n        metadata = index.lstat()\n    except FileNotFoundError:\n        metadata = None\n    if metadata is not None:\n        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):\n            raise SandboxError(\"target .gitnexus path must be a real directory before graph preparation\")\n        shutil.rmtree(index)\n    _replace_control_file(root, \".gitnexusrc\", b\"{}\\n\")\n    _replace_control_file(root, \".gitnexusignore\", b\"\")\n\n\ndef _scrub_source_references(root: Path) -> tuple[str, ...]:\n    \"\"\"Remove graph inputs whose path or stored content references the harness.\n\n    The disposable graph seed may contain docs or shipped skill copies outside\n    the removed harness that name its paths. They are harmless implementation\n    context in an arm checkout, but indexing them would let graph/MCP queries\n    recover benchmark-specific hints. Scan the exact <=512 KiB file universe\n    admitted by the pinned analyzer and remove contaminated inputs before the\n    graph is built. Target-controlled ignore/config files are not consulted.\n    \"\"\"\n\n    marker_bytes = tuple(marker.encode() for marker in GRAPH_MARKERS)\n    pending: list[tuple[Path, PurePosixPath]] = [(root, PurePosixPath())]","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/sanitized_graph.py#L123-L159","documentation":"Precondition guard in _neutralize_target_index_inputs. The clone's '.gitnexus' path, if present, must be a real directory (not a symlink, not a regular file) before the harness removes it and writes neutral control files. A symlinked or file '.gitnexus' is treated as a sandbox-escape attempt (pointing at an external graph) and rejected. An absent '.gitnexus' is fine: the harness simply creates the control files.","triggerScenarios":"A sanitized clone root contains a '.gitnexus' that lstat reports as a symlink or as a non-directory inode. This typically means a task fixture or sandbox_copy entry planted a '.gitnexus' link/file to smuggle in a prebuilt graph.","commonSituations":"A task tries to seed a graph by symlinking .gitnexus -> /some/prebuilt; a fixture created .gitnexus as a marker file; a sandbox_copy of a parent directory dragged in a .gitnexus regular file; a previous failed run left a stale file (less likely, since rmtree would have handled a dir).","solutions":["Inspect the clone root before neutralization: 'ls -la <clone>/.gitnexus' and confirm it is either absent or a real directory.","Remove any symlink or regular-file '.gitnexus' from the task fixture or sandbox_copy source.","Ensure no sandbox_dependency target is '.gitnexus' and no sandbox_copy entry creates it.","Let the harness build the graph in-sandbox; do not pre-populate .gitnexus by any mechanism."],"exampleFix":"# before (fixture pre-seeds a graph via symlink)\nln -s /prebuilt/graph .gitnexus\n# after (let the harness create and index from scratch)\nrm .gitnexus","handlingStrategy":"validation","validationCode":"from pathlib import Path\nimport stat\n\nclone = Path(\"clone-root\")\ng = clone / \".gitnexus\"\nif g.is_symlink():\n    raise SystemExit(\".gitnexus is a symlink; remove it\")\ntry:\n    mode = g.lstat().st_mode\nexcept FileNotFoundError:\n    pass\nelse:\n    if not stat.S_ISDIR(mode):\n        raise SystemExit(\".gitnexus must be a real directory or absent\")","typeGuard":"from pathlib import Path\nimport stat\n\ndef gitnexus_is_safe_to_neutralize(clone_root: Path) -> bool:\n    g = clone_root / \".gitnexus\"\n    try:\n        mode = g.lstat().st_mode\n    except FileNotFoundError:\n        return True\n    return stat.S_ISDIR(mode) and not stat.S_ISLNK(mode)","tryCatchPattern":"try:\n    _neutralize_target_index_inputs(clone_root)\nexcept SandboxError as exc:\n    if \"real directory before graph preparation\" in str(exc):\n        log.error(\".gitnexus is a symlink/file (possible escape attempt); remove it\")\n    raise","preventionTips":["Never pre-populate .gitnexus in a fixture, sandbox_copy, or dependency.","Let the harness build the graph from scratch inside the sandbox.","Audit fixtures for any '.gitnexus' symlink or regular file before submission."],"tags":["sandbox","graph","sandbox-escape","symlink","security","workflow-bench","gitnexus"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}