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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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