What
A downstream consumer — kaish-tools-git (kaish-extras), the read-only git tool built on gitoxide plumbing — needs stronger path containment than a plugin can build for itself, and the honest home for it is a kaish VFS primitive that every embedder could use.
Why it surfaced
kaish-tools-git must never read outside the VFS mount root ("ceiling") even for a hostile repository (the normal case for a codebase-analysis agent). A four-round cross-model review of its PR #20 (kaish-extras) found and closed four escape classes where repository-controlled content redirected reads outside the mount:
.git/commondir naming an absolute/.. path outside the ceiling;
- a symlink inside
.git that a lexical check walks through;
- the
commondir file itself being a symlink (read before any check on where it points);
objects/info/alternates naming an outside object store (no symlink, no gix off-switch).
All four are fixed in the tool by canonicalize-and-ceiling-check over three primitives (directories, fixed-name leaves via lstat, content-named paths).
The residual the tool cannot close itself
After those fixes, a hostile repo can still symlink a leaf that gitoxide opens internally — loose objects under objects/xx/, individual ref files, HEAD, packfiles — to a path outside the ceiling. The tool cannot intercept gix's own open() calls, and gix-odb / gix-ref expose no symlink-containment option (verified against gix-odb/gix-ref init Options; gix-sec gates ownership trust, not path containment).
Exfiltration reality for the read verbs is low (an arbitrary host file fails to zlib-inflate / ref-validate, so it errors rather than yielding content), and TOCTOU (canonicalize-then-open) is inherent and at parity with kaish's own LocalFs today — so the kaish-extras read profile ships accepting this carve-out, documented at the gix-open site and in its README (Amy's call, 2026-08-02).
But it is a genuine boundary, and it should be closed before write profiles or any hard multi-tenant use.
Proposed shape
A kaish VFS primitive that hands a consumer a RESOLVE_BENEATH-scoped view of a mount (Linux openat2(RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS), 5.6+), so every open a consumer (or a library it drives, like gix) performs is kernel-refused if it escapes the mount root — closing the residual structurally rather than by best-effort canonicalize-checking. kaish already owns resolve_real_path and LocalFs containment, so this belongs here, serves every embedder, and would let kaish-tools-git drop its hand-rolled leaf/content containment entirely.
Design considerations:
- Non-Linux fallback (the guarantee degrades to today's canonicalize-then-open parity; must be explicit, not silent).
- Interaction with the ledger-era
ToolCtx work — a scoped-view handle is plausibly something a tool acquires through ToolCtx.
- Whether gix can be threaded a dirfd, or whether the seam is a process/mount-level sandbox the consumer opts into.
Not urgent for the read profile; a prerequisite for the write profile and for treating kaish-tools-git as a hard sandbox boundary.
Filed from a kaish-extras design session; the four-escape detail and the tool-side fixes are in kaish-extras PR #20.
What
A downstream consumer —
kaish-tools-git(kaish-extras), the read-only git tool built on gitoxide plumbing — needs stronger path containment than a plugin can build for itself, and the honest home for it is a kaish VFS primitive that every embedder could use.Why it surfaced
kaish-tools-git must never read outside the VFS mount root ("ceiling") even for a hostile repository (the normal case for a codebase-analysis agent). A four-round cross-model review of its PR #20 (kaish-extras) found and closed four escape classes where repository-controlled content redirected reads outside the mount:
.git/commondirnaming an absolute/..path outside the ceiling;.gitthat a lexical check walks through;commondirfile itself being a symlink (read before any check on where it points);objects/info/alternatesnaming an outside object store (no symlink, no gix off-switch).All four are fixed in the tool by canonicalize-and-ceiling-check over three primitives (directories, fixed-name leaves via lstat, content-named paths).
The residual the tool cannot close itself
After those fixes, a hostile repo can still symlink a leaf that gitoxide opens internally — loose objects under
objects/xx/, individual ref files,HEAD, packfiles — to a path outside the ceiling. The tool cannot intercept gix's ownopen()calls, and gix-odb / gix-ref expose no symlink-containment option (verified against gix-odb/gix-ref init Options;gix-secgates ownership trust, not path containment).Exfiltration reality for the read verbs is low (an arbitrary host file fails to zlib-inflate / ref-validate, so it errors rather than yielding content), and TOCTOU (canonicalize-then-open) is inherent and at parity with kaish's own
LocalFstoday — so the kaish-extras read profile ships accepting this carve-out, documented at the gix-open site and in its README (Amy's call, 2026-08-02).But it is a genuine boundary, and it should be closed before write profiles or any hard multi-tenant use.
Proposed shape
A kaish VFS primitive that hands a consumer a
RESOLVE_BENEATH-scoped view of a mount (Linuxopenat2(RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS), 5.6+), so every open a consumer (or a library it drives, like gix) performs is kernel-refused if it escapes the mount root — closing the residual structurally rather than by best-effort canonicalize-checking. kaish already ownsresolve_real_pathandLocalFscontainment, so this belongs here, serves every embedder, and would let kaish-tools-git drop its hand-rolled leaf/content containment entirely.Design considerations:
ToolCtxwork — a scoped-view handle is plausibly something a tool acquires throughToolCtx.Not urgent for the read profile; a prerequisite for the write profile and for treating kaish-tools-git as a hard sandbox boundary.
Filed from a kaish-extras design session; the four-escape detail and the tool-side fixes are in kaish-extras PR #20.