{"record":{"id":"5bf2da0a3e9e7e6a","repo":"abhigyanpatwari/GitNexus","slug":"generated-artifact-is-not-a-regular-non-symlink-fi","errorCode":null,"errorMessage":"generated artifact is not a regular non-symlink file: {path}","messagePattern":"generated artifact is not a regular non-symlink file: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"eval/workflow_bench/runner_artifacts.py","lineNumber":436,"sourceCode":"            \"--no-ext-diff\",\n            \"--no-textconv\",\n            \"--shortstat\",\n            orig_sha,\n            \"--\",\n            \".\",\n            \":(exclude)docs/plans\",\n            \":(exclude).claude/skills\",\n        ],\n    )\n    return parse_shortstat(output)\n\n\ndef _bounded_regular_bytes(path: Path, *, limit: int) -> bytes:\n    \"\"\"Read at most ``limit`` bytes without following a generated link.\"\"\"\n\n    metadata = path.lstat()\n    if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode):\n        raise RuntimeError(f\"generated artifact is not a regular non-symlink file: {path}\")\n    nofollow = getattr(os, \"O_NOFOLLOW\", 0)\n    descriptor = os.open(path, os.O_RDONLY | nofollow)\n    try:\n        opened = os.fstat(descriptor)\n        if not stat.S_ISREG(opened.st_mode):\n            raise RuntimeError(f\"generated artifact changed type while opening: {path}\")\n        chunks: list[bytes] = []\n        remaining = limit\n        while remaining > 0:\n            chunk = os.read(descriptor, min(64 * 1024, remaining))\n            if not chunk:\n                break\n            chunks.append(chunk)\n            remaining -= len(chunk)\n        return b\"\".join(chunks)\n    finally:\n        os.close(descriptor)\n","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/runner_artifacts.py#L418-L454","documentation":"Thrown by _bounded_regular_bytes (used when reading the captured final patch) at the lstat gate. The harness refuses to follow a symlink or read a non-regular file as a generated artifact, because a symlink could point outside the worktree and a special file could exfiltrate or corrupt data. Only a plain regular file is accepted.","triggerScenarios":"path.lstat() reports S_ISLNK (the patch is a symlink) or not S_ISREG (it's a directory/socket/device/fifo). The agent-created patch is not a real file.","commonSituations":"The agent symlinked final.patch to /etc/passwd or another clone's patch; the patch path names a directory; a fifo/socket was created by a buggy tool; the artifact path collides with an existing directory.","solutions":["Have the agent write the patch as a real file (open in 'xb'/'wb' mode), not via ln -s.","Remove any pre-existing symlink at the patch path before capture.","Audit agent tooling for shell `ln -s` usage against artifact paths.","If the agent legitimately needs to reference another file, copy its bytes rather than linking."],"exampleFix":"// before — agent symlinks the patch\nos.symlink('/host/secret', worktree/'final.patch')\n\n// after — write real bytes\n(worktree/'final.patch').write_bytes(patch_bytes)","handlingStrategy":"validation","validationCode":"import stat\nfrom pathlib import Path\n\ndef is_regular_nonsymlink(path: Path) -> bool:\n    try:\n        st = path.lstat()\n    except OSError:\n        return False\n    return stat.S_ISREG(st.st_mode)","typeGuard":"import stat\nfrom pathlib import Path\n\ndef is_regular_nonsymlink(path: Path) -> bool:\n    try:\n        st = path.lstat()\n    except OSError:\n        return False\n    return stat.S_ISREG(st.st_mode)","tryCatchPattern":"try:\n    data = _bounded_regular_bytes(patch, limit=MAX_PATCH_BYTES)\nexcept RuntimeError as e:\n    if 'not a regular non-symlink file' in str(e):\n        # the agent produced a symlink/special file; rewrite as real bytes\n        raise\n    raise","preventionTips":["Write artifacts as real files (open 'wb'/'xb'), never via os.symlink.","Remove any pre-existing symlink at artifact paths before capture.","Audit agent shell usage for `ln -s` against artifact filenames.","Assert is_regular_nonsymlink before reading agent-produced files."],"tags":["security","symlink","sandbox-isolation","toctou","workflow-bench"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}