{"record":{"id":"4273f732d5c3c685","repo":"abhigyanpatwari/GitNexus","slug":"transcript-artifact-must-be-owner-only-path","errorCode":null,"errorMessage":"transcript artifact must be owner-only: {path}","messagePattern":"transcript artifact must be owner-only: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"critical","filePath":"eval/workflow_bench/evolve.py","lineNumber":396,"sourceCode":"            if normalized in seen_paths:\n                raise SandboxError(f\"duplicate transcript artifact path: {normalized}\")\n            seen_paths.add(normalized)\n        artifacts_by_row.append(artifacts)\n    return artifacts_by_row\n\n\ndef _bound_transcript_artifact(root: Path, metadata: Any) -> str:\n    relative, expected_digest, expected_size = _transcript_artifact_metadata(metadata)\n\n    path = _results_artifact_path(root, relative, transcript=True)\n    try:\n        before = path.lstat()\n    except OSError as exc:\n        raise SandboxError(f\"transcript artifact is unavailable: {path}: {exc}\") from exc\n    if stat.S_ISLNK(before.st_mode) or not stat.S_ISREG(before.st_mode):\n        raise SandboxError(f\"transcript artifact must be a regular non-symlink file: {path}\")\n    if stat.S_IMODE(before.st_mode) & 0o077:\n        raise SandboxError(f\"transcript artifact must be owner-only: {path}\")\n    if before.st_size != expected_size:\n        raise SandboxError(f\"transcript artifact size does not match its results row: {path}\")\n\n    descriptor = os.open(path, os.O_RDONLY | getattr(os, \"O_NOFOLLOW\", 0))\n    try:\n        opened = os.fstat(descriptor)\n        if not stat.S_ISREG(opened.st_mode) or opened.st_dev != before.st_dev or opened.st_ino != before.st_ino:\n            raise SandboxError(f\"transcript artifact changed while opening: {path}\")\n        digest = hashlib.sha256()\n        content = bytearray()\n        while chunk := os.read(descriptor, 64 * 1024):\n            digest.update(chunk)\n            content.extend(chunk)\n            if len(content) > MAX_EVIDENCE_FILE_BYTES:\n                del content[: len(content) - MAX_EVIDENCE_FILE_BYTES]\n        after = os.fstat(descriptor)\n        if (opened.st_size, opened.st_mtime_ns) != (after.st_size, after.st_mtime_ns):\n            raise SandboxError(f\"transcript artifact changed while reading: {path}\")","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/evolve.py#L378-L414","documentation":"Raised by `_bound_transcript_artifact` when the transcript file's permission bits include any group/other access (`mode & 0o077` is nonzero). Transcripts must be owner-only so no other local user can read proposer evidence.","triggerScenarios":"A transcript file with mode like 0644 or 0660 (any bit in group or other) under transcripts/. Common after unpacking a tarball that dropped owner-only bits.","commonSituations":"Extracting results from an archive without `--no-same-permissions` corrected, copying through a shared volume that relaxes modes, or a umask of 0022 producing 0644 files.","solutions":["Run `chmod go-rwx` (chmod 0600) on every file under the results dir's transcripts/ subtree.","Re-run the benchmark so the runner writes owner-only files directly.","Fix the umask (e.g. umask 0077) of whatever process stages the results dir."],"exampleFix":"// before: transcripts/foo.jsonl has mode 0644\nchmod -R go-rwx <seed-results>/transcripts\n// after: mode 0600 (owner-only)","handlingStrategy":"validation","validationCode":"import stat\nleaky = []\nfor r in evidence:\n    for a in r.get('transcript_artifacts', []):\n        m = (results_dir / a['path']).lstat().st_mode\n        if stat.S_IMODE(m) & 0o077:\n            leaky.append((a['path'], oct(stat.S_IMODE(m))))\nif leaky:\n    raise PermissionError(f'transcripts not owner-only: {leaky}')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set umask 0077 in any CI/script that writes results.","After copying a results dir, run `chmod -R go-rwx` before feeding it to evolve."],"tags":["python","workflow-bench","sandbox","security","permissions","evidence"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}