{"record":{"id":"e664f86ad3c44eba","repo":"abhigyanpatwari/GitNexus","slug":"label-must-be-a-repository-relative-path-relat","errorCode":null,"errorMessage":"{label} must be a repository-relative path: {relative!r}","messagePattern":"(.+?) must be a repository-relative path: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/proposer_sandbox.py","lineNumber":564,"sourceCode":"    try:\n        mode = lexical.lstat().st_mode\n    except OSError as exc:\n        raise SandboxError(f\"{label} must be a real directory: {lexical}: {exc}\") from exc\n    if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode):\n        raise SandboxError(f\"{label} must be a real directory: {lexical}\")\n    try:\n        resolved = lexical.resolve(strict=True)\n    except OSError as exc:\n        raise SandboxError(f\"{label} must be a real directory: {lexical}: {exc}\") from exc\n    if resolved != lexical:\n        raise SandboxError(f\"{label} must not traverse symlinks: {lexical}\")\n    return lexical\n\n\ndef _safe_repo_source(repo: Path, relative: str, *, label: str) -> tuple[Path, Path]:\n    candidate = PurePosixPath(relative)\n    if candidate.is_absolute() or \"..\" in candidate.parts or not candidate.parts:\n        raise SandboxError(f\"{label} must be a repository-relative path: {relative!r}\")\n    lexical = repo / Path(*candidate.parts)\n    resolved = lexical.resolve()\n    try:\n        resolved.relative_to(repo)\n    except ValueError as exc:\n        raise SandboxError(f\"{label} escapes its allowed repository root: {relative}\") from exc\n    if not resolved.exists():\n        raise SandboxError(f\"{label} does not exist: {relative}\")\n    return lexical, resolved\n\n\ndef _prepare_clone_target(\n    clone: Path,\n    relative: PurePosixPath,\n    *,\n    directory: bool | None,\n    label: str,\n) -> Path:","sourceCodeStart":546,"sourceCodeEnd":582,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/proposer_sandbox.py#L546-L582","documentation":"Raised by _safe_repo_source when the relative path is not a valid repository-relative path: candidate.is_absolute() is true, '..' appears in candidate.parts, or candidate.parts is empty. This stops absolute paths, parent-traversal, and empty inputs before any filesystem access, preventing escape from the repository root.","triggerScenarios":"Calling _safe_repo_source with relative='/' (absolute), '/etc/passwd', '../outside', 'a/../../b' (after PurePosixPath normalizes parts), or '' (empty -> no parts).","commonSituations":"Caller used str(path) of an absolute Path as the relative argument; user-supplied or config-supplied path string contained '..'; an empty string passed from an unset config key; a path was normalized externally with PurePosixPath and the '..' collapsed oddly.","solutions":["Pass a true relative path with no '..' segments: derive it via path.relative_to(repo).","Validate before calling: assert not PurePosixPath(rel).is_absolute() and '..' not in PurePosixPath(rel).parts and PurePosixPath(rel).parts.","Reject empty input upstream with a clear config error.","If you genuinely need a parent path, change the design — the boundary forbids it by intent."],"exampleFix":"// before\nsrc, resolved = _safe_repo_source(repo, str(some_abs_path), label='task asset')\n// after\nrel = PurePosixPath(some_abs_path).relative_to(repo)\nsrc, resolved = _safe_repo_source(repo, str(rel), label='task asset')","handlingStrategy":"validation","validationCode":"from pathlib import PurePosixPath\n\ndef is_repo_relative(rel: str) -> bool:\n    p = PurePosixPath(rel)\n    return (not p.is_absolute()\n            and '..' not in p.parts\n            and len(p.parts) > 0\n            and p.name not in {'', '.', '..'})\n\nassert is_repo_relative(relative)","typeGuard":"from pathlib import PurePosixPath\n\ndef is_safe_repo_relative(value: object) -> bool:\n    if not isinstance(value, str):\n        return False\n    p = PurePosixPath(value)\n    return (not p.is_absolute()\n            and '..' not in p.parts\n            and len(p.parts) > 0\n            and p.name not in {'', '.', '..'})","tryCatchPattern":"try:\n    src, resolved = _safe_repo_source(repo, relative, label=label)\nexcept SandboxError as exc:\n    if 'repository-relative path' in str(exc):\n        rel = PurePosixPath(relative).relative_to(repo) if PurePosixPath(relative).is_absolute() else PurePosixPath(relative)\n        src, resolved = _safe_repo_source(repo, str(rel), label=label)\n    raise","preventionTips":["Derive relative paths via path.relative_to(repo).","Reject '..' and absolute paths at input parse time.","Treat empty path strings as config errors.","Add unit tests for traversal inputs."],"tags":["path-traversal","security","validation","sandbox"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}