abhigyanpatwari/GitNexus · error · SandboxError

unsafe results artifact path: {relative_value!r}

Error message

unsafe results artifact path: {relative_value!r}

What it means

_results_artifact_path enforces a strict shape on each artifact's relative path before any filesystem touch: not absolute, exactly expected_parts (1 for a plain artifact, 2 for a transcript), no empty/./.. components, and transcripts must start with 'transcripts/'. This is the primary path-traversal and layout guard for results.jsonl entries.

Source

Thrown at eval/workflow_bench/evolve.py:310

    except OSError as exc:
        raise SandboxError(f"results directory is unavailable: {root}: {exc}") from exc
    if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
        raise SandboxError(f"results directory must be a real non-symlink directory: {root}")
    if root.resolve(strict=True) != root:
        raise SandboxError(f"results directory must not traverse symlinks: {root}")
    return root


def _results_artifact_path(root: Path, relative_value: str, *, transcript: bool) -> Path:
    relative = PurePosixPath(relative_value)
    expected_parts = 2 if transcript else 1
    if (
        relative.is_absolute()
        or len(relative.parts) != expected_parts
        or any(part in {"", ".", ".."} for part in relative.parts)
        or (transcript and relative.parts[0] != "transcripts")
    ):
        raise SandboxError(f"unsafe results artifact path: {relative_value!r}")
    current = root
    for part in relative.parts[:-1]:
        current /= part
        try:
            metadata = current.lstat()
        except OSError as exc:
            raise SandboxError(f"results artifact parent is unavailable: {current}: {exc}") from exc
        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
            raise SandboxError(f"results artifact parent must be a real directory: {current}")
        if transcript and stat.S_IMODE(metadata.st_mode) & 0o077:
            raise SandboxError(f"transcript artifact parent must be owner-only: {current}")
    return root / Path(*relative.parts)


def _transcript_artifact_metadata(metadata: Any) -> tuple[str, str, int]:
    """Validate transcript metadata without touching any host path."""

    if not isinstance(metadata, dict) or set(metadata) != {"path", "sha256", "bytes", "source"}:

View on GitHub (pinned to d540b00184)

Solutions

  1. Inspect the offending row: the message prints relative_value!r; find that exact string in results.jsonl.
  2. Rewrite the row's path to a single component (plain artifact) or 'transcripts/<name>' (transcript).
  3. Ensure the runner that writes results.jsonl normalizes paths to the contract.

Example fix

# before (results.jsonl row)
{"path": "../hidden/secret", "sha256": "...", ...}

# after
{"path": "secret", "sha256": "...", ...}
# and for transcripts
{"path": "transcripts/run-42.json", "source": "parent-captured-stream-json", ...}
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import PurePosixPath

def artifact_path_ok(relative_value: str, *, transcript: bool) -> bool:
    p = PurePosixPath(relative_value)
    expected = 2 if transcript else 1
    return (
        not p.is_absolute()
        and len(p.parts) == expected
        and not any(part in {"", ".", ".."} for part in p.parts)
        and (not transcript or p.parts[0] == "transcripts")
    )

Prevention

When it happens

Trigger: A results row carries a path like '../secret', '/etc/passwd', 'a/b/c' (too many parts), '' or '.', or a transcript path that does not start with 'transcripts/'.

Common situations: A custom runner emits absolute paths or nested layouts; a hand-edited results.jsonl; a transcript path recorded without the transcripts/ prefix; path-escape attempts in crafted evidence.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/1b9267d9a1694bef. Report an issue: GitHub.