{"record":{"id":"a63e0d0740bb4b24","repo":"abhigyanpatwari/GitNexus","slug":"extra-read-only-mount-target-must-be-absolute-mo","errorCode":null,"errorMessage":"extra read-only mount target must be absolute: {mount.target}","messagePattern":"extra read-only mount target must be absolute: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/proposer_sandbox.py","lineNumber":158,"sourceCode":"                )\n            )\n\n        for mount in extra_read_only_mounts:\n            source = mount.source.expanduser().absolute()\n            try:\n                metadata = source.lstat()\n                resolved = source.resolve(strict=True)\n            except OSError as exc:\n                raise SandboxError(f\"extra read-only mount is unavailable: {source}\") from exc\n            if (\n                resolved != source\n                or stat.S_ISLNK(metadata.st_mode)\n                or not (stat.S_ISDIR(metadata.st_mode) or stat.S_ISREG(metadata.st_mode))\n            ):\n                raise SandboxError(f\"extra read-only mount must be real and non-symlink: {source}\")\n            target = PurePosixPath(mount.target)\n            if not target.is_absolute() or \"..\" in target.parts:\n                raise SandboxError(f\"extra read-only mount target must be absolute: {mount.target}\")\n            additional.append(ReadOnlyMount(source=source, target=target.as_posix()))\n\n        return _sandbox_command_prefix(\n            bwrap=self.bwrap_bin,\n            clone=clone,\n            home=self.home,\n            temp=self.temp,\n            claude_bin=self.claude_host_bin,\n            mounts=(*self.read_only_mounts, *additional),\n            read_only_workspace=read_only_workspace,\n            unshare_network=unshare_network,\n        )\n\n\n_TOKEN_PATTERNS = (\n    re.compile(r\"sk-ant-[A-Za-z0-9_-]{8,}\"),\n    re.compile(r\"gh(?:p|o|u|s|r)_[A-Za-z0-9_]{8,}\"),\n    re.compile(r\"(?i)(authorization\\s*[:=]\\s*(?:bearer\\s+)?)[^\\s,;]+\"),","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/proposer_sandbox.py#L140-L176","documentation":"Thrown by `command_prefix_for` when an `extra_read_only_mounts` target is not an absolute path or contains a `..` component. Bubblewrap `--ro-bind` targets inside the sandbox must be absolute and must not allow path traversal outside the workspace root.","triggerScenarios":"Calling `command_prefix_for(extra_read_only_mounts=[ReadOnlyMount(source=s, target=t)])` where `t` is relative (e.g. `oracle.json`), or contains `..` (e.g. `/workspace/../etc/passwd`). The guard at proposer_sandbox.py:157 rejects both.","commonSituations":"Passing a target like `str(path)` where `path` was relative by mistake; constructing the target from user input without normalizing; a target that tries to escape the sandbox via `..`; forgetting the leading slash.","solutions":["Make the target absolute and workspace-rooted: `target = f'{SANDBOX_WORKSPACE}/{name}'` with no `..` parts.","Validate before constructing the mount: `t = PurePosixPath(target); assert t.is_absolute() and '..' not in t.parts`.","Normalize user-supplied targets: `target = str(Path('/' + target).resolve())` to coerce absolute + strip `..`.","If you intended a relative target, you must convert it — bubblewrap does not accept relative mount targets."],"exampleFix":"# before: relative / traversal target\nmount = ReadOnlyMount(source=oracle, target='oracle.json')  # -> SandboxError\n# or\nmount = ReadOnlyMount(source=oracle, target='/workspace/../etc/oracle')  # -> SandboxError\n# after: absolute, workspace-rooted, no '..'\nfrom pathlib import PurePosixPath\ntarget = PurePosixPath('/workspace/oracle.json')\nassert target.is_absolute() and '..' not in target.parts\nmount = ReadOnlyMount(source=oracle, target=target.as_posix())","handlingStrategy":"validation","validationCode":"from pathlib import PurePosixPath\n\ndef mount_target_is_safe(target: str) -> bool:\n    t = PurePosixPath(target)\n    return t.is_absolute() and \"..\" not in t.parts\n\nmounts = [m for m in mounts if mount_target_is_safe(m.target)]\nprefix = session.command_prefix_for(extra_read_only_mounts=mounts)","typeGuard":"from pathlib import PurePosixPath\n\ndef is_safe_mount_target(target: str) -> bool:\n    \"\"\"True iff target is absolute and contains no '..' (no traversal).\"\"\"\n    t = PurePosixPath(target)\n    return t.is_absolute() and \"..\" not in t.parts","tryCatchPattern":"try:\n    prefix = session.command_prefix_for(extra_read_only_mounts=mounts)\nexcept SandboxError as exc:\n    if \"target must be absolute\" in str(exc):\n        # coerce to absolute, workspace-rooted, no '..'\n        from pathlib import Path\n        mounts = [\n            ReadOnlyMount(\n                source=m.source,\n                target=str(Path(\"/\" + m.target).resolve()),\n            )\n            for m in mounts\n        ]\n        prefix = session.command_prefix_for(extra_read_only_mounts=mounts)\n    raise","preventionTips":["Always build targets as `f\"{SANDBOX_WORKSPACE}/{name}\"` with no `..`.","Normalize user-supplied targets: `str(Path('/' + target).resolve())`.","Reject relative targets at the API boundary of your harness."],"tags":["sandbox","bwrap","path-traversal","mount","validation","security"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}