{"record":{"id":"0e847b7c20be67d7","repo":"abhigyanpatwari/GitNexus","slug":"evidence-names-must-be-simple-relative-files-nam","errorCode":null,"errorMessage":"evidence names must be simple relative files: {name!r}","messagePattern":"evidence names must be simple relative files: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/proposer_sandbox.py","lineNumber":230,"sourceCode":"def stage_evidence_bundle(\n    destination: Path,\n    entries: Mapping[str, Any],\n    *,\n    secrets: Sequence[str] = (),\n) -> Path:\n    \"\"\"Write a redacted owner-only evidence bundle with hard byte caps.\"\"\"\n\n    destination = destination.resolve()\n    if destination.exists():\n        raise SandboxError(f\"evidence destination already exists: {destination}\")\n    destination.mkdir(parents=True, mode=0o700)\n    destination.chmod(0o700)\n    total = 0\n    try:\n        for name, value in entries.items():\n            relative = PurePosixPath(name)\n            if len(relative.parts) != 1 or relative.name in {\"\", \".\", \"..\"}:\n                raise SandboxError(f\"evidence names must be simple relative files: {name!r}\")\n            payload = _evidence_bytes(value, secrets)\n            if len(payload) > MAX_EVIDENCE_FILE_BYTES:\n                raise SandboxError(f\"evidence exceeds the per-file limit: {name}\")\n            total += len(payload)\n            if total > MAX_BUNDLE_BYTES:\n                raise SandboxError(\"evidence bundle exceeds the total byte limit\")\n            path = destination / relative.name\n            path.write_bytes(payload)\n            path.chmod(0o600)\n    except BaseException:\n        shutil.rmtree(destination, ignore_errors=True)\n        raise\n    return destination\n\n\ndef _validated_base_url(base_url: str) -> str:\n    value = base_url.strip()\n    parsed = urlsplit(value)","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/proposer_sandbox.py#L212-L248","documentation":"Raised by stage_evidence_bundle when an entry name is not a single simple relative filename. PurePosixPath(name) must have exactly one part whose name is not '', '.', or '..'. This blocks path traversal (../), absolute paths (/etc/...), nested subpaths (sub/file), and empty names so an entry cannot escape the destination directory or create unexpected structure.","triggerScenarios":"Passing an entries mapping whose key contains a slash, is absolute, contains a '..' segment, or is empty/dot/dotdot. e.g. {\"../escape\": ...}, {\"/etc/passwd\": ...}, {\"a/b\": ...}, {\"\": ...}.","commonSituations":"Caller reused a full path as the key instead of basename; generated name from untrusted/external data without sanitization; key built by string concatenation that produced a leading slash; normalized name that PurePosixPath splits into multiple parts.","solutions":["Use only the file's basename as the key: {path.name: path} or PurePosixPath(name).name.","Sanitize any externally sourced name: strip slashes, reject '..' and empty, keep one part.","Build keys from a controlled allowlist of filenames.","Assert len(PurePosixPath(name).parts) == 1 before building the mapping."],"exampleFix":"// before\nentries = {str(p): p for p in paths}  # may include 'dir/x'\n// after\nentries = {p.name: p for p in paths}\nassert all(len(PurePosixPath(k).parts) == 1 and k not in {'','.','..'} for k in entries)","handlingStrategy":"validation","validationCode":"from pathlib import PurePosixPath\n\ndef is_simple_name(name: str) -> bool:\n    p = PurePosixPath(name)\n    return len(p.parts) == 1 and p.name not in {'', '.', '..'}\n\nentries = {k: v for k, v in entries.items() if is_simple_name(k)}\n# or fail loud:\nassert all(is_simple_name(k) for k in entries), list(entries)","typeGuard":"from pathlib import PurePosixPath\n\ndef is_simple_relative_filename(name: object) -> bool:\n    if not isinstance(name, str):\n        return False\n    p = PurePosixPath(name)\n    return len(p.parts) == 1 and p.name not in {'', '.', '..'}","tryCatchPattern":"try:\n    stage_evidence_bundle(dest, entries, secrets=secrets)\nexcept SandboxError as exc:\n    if 'must be simple relative files' in str(exc):\n        entries = {PurePosixPath(k).name: v for k, v in entries.items()}\n        stage_evidence_bundle(dest, entries, secrets=secrets)\n    raise","preventionTips":["Always use path.name as the entry key.","Sanitize any externally sourced filename through PurePosixPath and assert one part.","Never build keys by string concatenation with separators.","Add a unit test that '..' and absolute names are rejected."],"tags":["evidence","path-traversal","security","validation"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}