abhigyanpatwari/GitNexus · critical · ValueError
overlay destination escapes repository: {target}
Error message
overlay destination escapes repository: {target} What it means
All errors below are raised by internal helpers of `eval/workflow_bench/promotion_apply.py` and propagate to the caller of the public entry points: `apply_promoted_overlay(overlay, repo_root, *, expected_digest, expected_target_bases)`, `destination_base_digests(overlay, repo_root)`, `committed_destination_base_digests(overlay, repo_root, *, ref)` and `freeze_overlay(overlay, destination)`. The module applies promoted skill overlays across the canonical skill tree plus its shipped mirrors (`gitnexus/skills`, `gitnexus-claude-plugin/skills`) in a TOCTOU-hardened, symlink-rejecting, descriptor-bound transaction. `_open_target_parent` rejects a target immediately when `target.is_absolute() or not target.parts or '..' in target.parts`. Targets derive from `mirror_targets(relative)` built off the overlay payload's relative paths (see `candidate_overlay_payload`). Absolute, empty, or `..`-bearing paths would escape the held repository-root descriptor, so they are refused before any directory walk.
Source
Thrown at eval/workflow_bench/promotion_apply.py:193
stat.S_ISLNK(final.st_mode)
or not stat.S_ISDIR(opened.st_mode)
or not stat.S_ISDIR(final.st_mode)
or final_resolved != root
or not (identity(metadata) == identity(opened) == identity(final))
):
raise ValueError(f"repository root changed while opening: {root}")
except OSError as exc:
os.close(descriptor)
raise ValueError(f"repository root changed while opening: {root}") from exc
except BaseException:
os.close(descriptor)
raise
return root, descriptor
def _open_target_parent(root_descriptor: int, target: PurePosixPath) -> int:
if target.is_absolute() or not target.parts or ".." in target.parts:
raise ValueError(f"overlay destination escapes repository: {target}")
flags = os.O_RDONLY | os.O_DIRECTORY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0)
current = os.dup(root_descriptor)
try:
for part in target.parts[:-1]:
try:
metadata = os.stat(part, dir_fd=current, follow_symlinks=False)
except OSError as exc:
raise ValueError(f"overlay destination parent is unavailable: {target}") from exc
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
raise ValueError(f"overlay destination parent must not be a symlink: {target}")
try:
child = os.open(part, flags, dir_fd=current)
except OSError as exc:
raise ValueError(f"overlay destination parent changed while opening: {target}") from exc
opened = os.fstat(child)
if (
opened.st_dev,
opened.st_ino,View on GitHub (pinned to d540b00184)
Solutions
- Inspect the overlay payload paths: `for r,_ in candidate_overlay_payload(overlay)[1]: print(r)`.
- Strip/normalize member names when building the overlay; reject absolute and `..` entries at overlay-authoring time.
- Re-create the overlay from a clean source tree using only repo-relative names.
Example fix
// before
# overlay contains an entry like b'../escape/SKILL.md'
apply_promoted_overlay(overlay, repo_root=root)
// after
_, payload = candidate_overlay_payload(overlay)
bad = [str(r) for r,_ in payload if r.is_absolute() or not r.parts or '..' in r.parts]
assert not bad, f'overlay has escaping entries: {bad}'
apply_promoted_overlay(overlay, repo_root=root) Defensive patterns
Strategy: validation
Validate before calling
def assert_no_escape(payload):
for rel, _ in payload:
assert not rel.is_absolute() and rel.parts and '..' not in rel.parts, f'escaping path: {rel}'
_, payload = candidate_overlay_payload(overlay)
assert_no_escape(payload)
apply_promoted_overlay(overlay, repo_root=root) Type guard
from pathlib import PurePosixPath
def overlay_paths_are_safe(payload) -> bool:
return all(
(not r.is_absolute()) and r.parts and ('..' not in r.parts)
for r, _ in payload
) Try / catch
except ValueError as exc:
if 'escapes repository' in str(exc):
raise SystemExit(f'refusing overlay with traversal entry: {exc}') Prevention
- Author overlays with strictly repo-relative, normalized member names only.
- Validate the payload with `overlay_paths_are_safe` before calling the API.
- Treat any escaping entry as a security incident, not a retryable error.
When it happens
Trigger: The overlay payload contains an absolute path, an empty relative path, or a path with `..` segments; equivalently the candidate overlay ZIP/tar declared a traversal entry.
Common situations: A hand-crafted or corrupted overlay file includes entries like `/etc/passwd`, `../../sibling`, or a bare empty name; a packaging tool wrote absolute member names.
Related errors
- Invalid group name "${name}". Names must start with a letter
- {label} contains an unsafe path component: {relative}
- candidate destination escapes the clone: {relative}
- unsafe results artifact path: {relative_value!r}
- overlay destination parent must not be a symlink: {target}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/f817863b088e6793.
Report an issue: GitHub.