abhigyanpatwari/GitNexus · error · SandboxError
evidence names must be simple relative files: {name!r}
Error message
evidence names must be simple relative files: {name!r} What it means
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.
Source
Thrown at eval/workflow_bench/proposer_sandbox.py:230
def stage_evidence_bundle(
destination: Path,
entries: Mapping[str, Any],
*,
secrets: Sequence[str] = (),
) -> Path:
"""Write a redacted owner-only evidence bundle with hard byte caps."""
destination = destination.resolve()
if destination.exists():
raise SandboxError(f"evidence destination already exists: {destination}")
destination.mkdir(parents=True, mode=0o700)
destination.chmod(0o700)
total = 0
try:
for name, value in entries.items():
relative = PurePosixPath(name)
if len(relative.parts) != 1 or relative.name in {"", ".", ".."}:
raise SandboxError(f"evidence names must be simple relative files: {name!r}")
payload = _evidence_bytes(value, secrets)
if len(payload) > MAX_EVIDENCE_FILE_BYTES:
raise SandboxError(f"evidence exceeds the per-file limit: {name}")
total += len(payload)
if total > MAX_BUNDLE_BYTES:
raise SandboxError("evidence bundle exceeds the total byte limit")
path = destination / relative.name
path.write_bytes(payload)
path.chmod(0o600)
except BaseException:
shutil.rmtree(destination, ignore_errors=True)
raise
return destination
def _validated_base_url(base_url: str) -> str:
value = base_url.strip()
parsed = urlsplit(value)View on GitHub (pinned to d540b00184)
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.
Example fix
// before
entries = {str(p): p for p in paths} # may include 'dir/x'
// after
entries = {p.name: p for p in paths}
assert all(len(PurePosixPath(k).parts) == 1 and k not in {'','.','..'} for k in entries) Defensive patterns
Strategy: validation
Validate before calling
from pathlib import PurePosixPath
def is_simple_name(name: str) -> bool:
p = PurePosixPath(name)
return len(p.parts) == 1 and p.name not in {'', '.', '..'}
entries = {k: v for k, v in entries.items() if is_simple_name(k)}
# or fail loud:
assert all(is_simple_name(k) for k in entries), list(entries) Type guard
from pathlib import PurePosixPath
def is_simple_relative_filename(name: object) -> bool:
if not isinstance(name, str):
return False
p = PurePosixPath(name)
return len(p.parts) == 1 and p.name not in {'', '.', '..'} Try / catch
try:
stage_evidence_bundle(dest, entries, secrets=secrets)
except SandboxError as exc:
if 'must be simple relative files' in str(exc):
entries = {PurePosixPath(k).name: v for k, v in entries.items()}
stage_evidence_bundle(dest, entries, secrets=secrets)
raise Prevention
- 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.
When it happens
Trigger: 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": ...}, {"": ...}.
Common situations: 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.
Related errors
- unsafe results artifact path: {relative_value!r}
- Invalid group name "${name}". Names must start with a letter
- {label} contains an unsafe path component: {relative}
- overlay destination escapes repository: {target}
- extra read-only mount target must be absolute: {mount.target
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/0e847b7c20be67d7.
Report an issue: GitHub.