Skip to content

VFS seam: RESOLVE_BENEATH-scoped mount view for symlink containment (surfaced by kaish-tools-git read profile) #276

Description

@tobert

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:

  1. .git/commondir naming an absolute/.. path outside the ceiling;
  2. a symlink inside .git that a lexical check walks through;
  3. the commondir file itself being a symlink (read before any check on where it points);
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions