Skip to content

boundless sandbox: .git exec-surface protection is a no-op on Windows (mxc has no write-only-deny for a subpath) #171

Description

@polaris-is-online

Summary

The boundless filesystem sandbox now keeps the Git exec-surface paths inside the working tree -- .git/hooks and .git/config -- read-only, even though the rest of cwd stays writable. Those paths hold code Git later runs as the operator, outside the sandbox: hook scripts, and config directives that execute a command (core.hooksPath, core.fsmonitor, core.sshCommand, core.pager, alias.* = "!cmd"). Confining them read-only closes the "plant a payload now, fire it on the operator's next git in the repo" path. Git's own commands only ever read them, so the carve-out costs nothing in normal work.

It is enforced on Linux (bubblewrap) and for boundless's in-process file tools (write/edit/copy) on every platform except Windows. On Windows it is a no-op: .git stays fully writable. This tracks closing that gap when mxc gains the primitive it needs.

Why Windows cannot express it today

The carve-out needs "readable but not writable" for a subpath of a writable parent. No mxc Windows backend can express that:

  • DACL / AppContainer tiers: only additive allow-ACLs plus a single full-access deny mask. A full-access deny on .git/config would also block Git's reads of config and break the repo. There is no write-only deny ACE in the model as wired.
  • base-container tier: grants writable subtrees; no mechanism to subtract a read-only hole from a granted-writable parent.

Verified by reading the Windows backends in microsoft/mxc (filesystem_dacl.rs, filesystem_bfs.rs, the appcontainer runner, the base-container share path).

On Linux it falls out for free: the bubblewrap builder emits readonly binds after readwrite binds and "later mounts win, overriding any rw parent" (src/backends/bubblewrap/common/src/bwrap_command.rs), so a .git/hooks ro-bind layered after the cwd rw-bind wins. On macOS (seatbelt) the kernel-layer carve-out is an inert allow-read -- seatbelt readonly cannot subtract an existing write grant -- but the in-process file-tool guard still covers write/edit/copy there.

Coverage matrix (current)

  • Linux: bash (bubblewrap ro-bind) + file tools (in-process) -- full
  • macOS: file tools (in-process) only; bash unprotected (seatbelt limitation)
  • Windows: neither -- .git fully writable

Where it lives

computeGitProtectedPaths in packages/less/src/tools/sandbox-policy.ts is the single source of the protected-path set and the Windows gate (platform === "win32" returns []). It feeds both buildPolicy (readonlyPaths, the mxc kernel guard) and checkWritePath (the in-process file-tool guard). Documented in README (boundless section) and CONTRIBUTING (Common Gotchas).

Resolution trigger

Revisit when mxc exposes a write-only-deny (or readable-but-not-writable subpath) primitive on a Windows backend. Then: drop the win32 short-circuit in computeGitProtectedPaths (or narrow it to whichever tiers still lack the primitive) and extend integration coverage to the Windows lane.

Threat-model note

This is defense-in-depth on one narrow escalation path, not a wall. An agent that can write arbitrary code into the working tree (source files, dist, node_modules) already achieves "runs as the operator later" the next time the operator builds or runs the project. The .git carve-out closes the most surprising version of that -- a hook firing on a plain git status -- not the general class. Calibrate accordingly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    boundlessRelated to the boundless coding agentbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions