{"record":{"id":"4bdc5ad9e46f1abb","repo":"abhigyanpatwari/GitNexus","slug":"results-artifact-parent-must-be-a-real-directory","errorCode":null,"errorMessage":"results artifact parent must be a real directory: {current}","messagePattern":"results artifact parent must be a real directory: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/evolve.py","lineNumber":319,"sourceCode":"def _results_artifact_path(root: Path, relative_value: str, *, transcript: bool) -> Path:\n    relative = PurePosixPath(relative_value)\n    expected_parts = 2 if transcript else 1\n    if (\n        relative.is_absolute()\n        or len(relative.parts) != expected_parts\n        or any(part in {\"\", \".\", \"..\"} for part in relative.parts)\n        or (transcript and relative.parts[0] != \"transcripts\")\n    ):\n        raise SandboxError(f\"unsafe results artifact path: {relative_value!r}\")\n    current = root\n    for part in relative.parts[:-1]:\n        current /= part\n        try:\n            metadata = current.lstat()\n        except OSError as exc:\n            raise SandboxError(f\"results artifact parent is unavailable: {current}: {exc}\") from exc\n        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):\n            raise SandboxError(f\"results artifact parent must be a real directory: {current}\")\n        if transcript and stat.S_IMODE(metadata.st_mode) & 0o077:\n            raise SandboxError(f\"transcript artifact parent must be owner-only: {current}\")\n    return root / Path(*relative.parts)\n\n\ndef _transcript_artifact_metadata(metadata: Any) -> tuple[str, str, int]:\n    \"\"\"Validate transcript metadata without touching any host path.\"\"\"\n\n    if not isinstance(metadata, dict) or set(metadata) != {\"path\", \"sha256\", \"bytes\", \"source\"}:\n        raise SandboxError(\"transcript artifact metadata must contain only path, sha256, bytes, and source\")\n    relative = metadata[\"path\"]\n    expected_digest = metadata[\"sha256\"]\n    expected_size = metadata[\"bytes\"]\n    if metadata[\"source\"] != runner_sessions.PARENT_EVENT_STREAM_SOURCE:\n        raise SandboxError(\"transcript artifact source is not the parent event stream\")\n    if not isinstance(relative, str) or not re.fullmatch(r\"[0-9a-f]{64}\", str(expected_digest)):\n        raise SandboxError(\"transcript artifact metadata is malformed\")\n    if not isinstance(expected_size, int) or isinstance(expected_size, bool):","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/evolve.py#L301-L337","documentation":"In the same parent walk, if a parent component's lstat mode is a symlink or not a directory, the harness refuses it. This blocks a path component that looks like a directory in results.jsonl but is actually a file or a symlink — a classic traversal/swap vehicle.","triggerScenarios":"An artifact path component is a regular file (e.g. 'transcripts' is a file, not a dir), or a symlink standing in for a directory; a crafted results tree trying to redirect through a link.","commonSituations":"A file accidentally created where a directory should be; a symlinked subdirectory; tampered evidence.","solutions":["Inspect the printed component: `ls -ldH <results_dir>/<component>` and confirm it is a real directory with no arrow.","Replace any symlinked component with a real directory and repopulate it.","Remove any file shadowing the directory name and recreate the directory."],"exampleFix":"# before: 'transcripts' is a symlink or a file\nls results/transcripts  # -> file or symlink\n\n# after\nrm -f results/transcripts\nmkdir -p results/transcripts\ncp /backup/transcripts/* results/transcripts/","handlingStrategy":"validation","validationCode":"import stat\nfrom pathlib import Path, PurePosixPath\n\ndef artifact_parents_are_real_dirs(root: Path, relative_value: str) -> bool:\n    current = root\n    for part in PurePosixPath(relative_value).parts[:-1]:\n        current /= part\n        m = current.lstat().st_mode\n        if stat.S_ISLNK(m) or not stat.S_ISDIR(m):\n            return False\n    return True","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never replace a results subdirectory with a file or symlink.","Keep the results tree on a single real filesystem with no symlinked components.","CI check: `find <results_dir> -type l` must be empty."],"tags":["workflow-bench","sandbox","security","filesystem","symlink","path-traversal"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}