{"record":{"id":"7777613213a30f3e","repo":"abhigyanpatwari/GitNexus","slug":"transcript-artifact-parent-must-be-owner-only-cu","errorCode":null,"errorMessage":"transcript artifact parent must be owner-only: {current}","messagePattern":"transcript artifact parent must be owner-only: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/evolve.py","lineNumber":321,"sourceCode":"    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):\n        raise SandboxError(\"transcript artifact byte count must be an integer\")\n    if expected_size < 0 or expected_size > runner.MAX_TRANSCRIPT_BYTES:","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/evolve.py#L303-L339","documentation":"For transcript artifacts only, _results_artifact_path requires the parent directory's permission bits to be owner-only: `stat.S_IMODE(mode) & 0o077` must be zero. Transcript content is sensitive (agent event streams), so a group/other-readable or writable parent is treated as a containment breach.","triggerScenarios":"The transcripts directory (or any ancestor in the artifact path) has group or other bits set: chmod g+r, o+r, g+w, etc.; results copied with a permissive umask; a shared volume with宽松 mode.","commonSituations":"Results tree on a shared/group-writable volume; files copied with `cp -a` from a permissive source; CI that runs chmod 775 on artifacts; tarballs extracted with group inheritance.","solutions":["Tighten the parent: `chmod 700 <results_dir>/transcripts` (or 0700/0500).","Re-extract any results tarball with `umask 077` first.","Audit the whole results tree: `find <results_dir> -type d -perm /077` and fix each hit."],"exampleFix":"# before\nchmod -R g+rwX results/transcripts  # group/other bits now set\n\n# after\nchmod 700 results/transcripts\nfind results -type d -perm /077 -exec chmod o-rwx,g-rwx {} +","handlingStrategy":"validation","validationCode":"import stat\nfrom pathlib import Path, PurePosixPath\n\ndef transcript_parents_owner_only(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_IMODE(m) & 0o077:\n            return False\n    return True\n\n# enforce before proposing\nimport os\nos.chmod(root / \"transcripts\", 0o700)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create transcript dirs with mode 0o700 from the start.","Extract results tarballs with `umask 077`.","Audit with `find <results_dir> -type d -perm /077` in CI."],"tags":["workflow-bench","sandbox","security","permissions","transcript","filesystem"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}