wrapper-guard: a checkout target that never names $HOME still lands in it #1197

Open
opened 2026-08-13 10:09:12 +00:00 by Mos · 0 comments
Contributor

Summary

wrapper-guard.sh refuses a checkout that lands under $HOME. Nine rounds on PR #1174
hardened the rule against every spelling of $HOME — quoted, braced, escaped, tilde,
literal, and every shell word boundary around them.

All nine rounds share an assumption that is false: that the command says where it is
going.
A relative target names no directory at all. It resolves against the cwd, and
every agent seat on this host runs with a cwd under $HOME.

Measurement

Guard at 20d86e39 (current PR #1174 head, i.e. after all nine rounds), cwd
/home/hermes/agent-work. want=2 is "must block", got=0 is "cleared".

no target at all (defaults to cwd/<repo>)  -> /home/hermes/agent-work/myrepo    want=2 got=0
bare relative name                         -> /home/hermes/agent-work/wt        want=2 got=0
explicit ./                                -> /home/hermes/agent-work/wt        want=2 got=0
the cwd itself                             -> /home/hermes/agent-work           want=2 got=0
relative subdir                            -> /home/hermes/agent-work/sub/wt    want=2 got=0
parent-relative                            -> /home/hermes/wt                   want=2 got=0
worktree add, relative                     -> /home/hermes/agent-work/wt        want=2 got=0

7 shapes, 7 uncovered

The first row is the one that matters most: a checkout command with no target argument
is the most ordinary form there is, and it lands in the cwd.

Why this was not folded into #1174

It is a different rule, not another spelling. Matching home tokens cannot express it —
the guard needs the effective cwd, and:

  1. The cwd available to a PreToolUse hook is the session cwd. Whether that field is
    delivered on PreToolUse specifically is unverified; qa/reflect-stop-hook.sh
    consumes .cwd // empty with a ${payload_cwd:-$PWD} fallback, which shows the field
    exists for at least one hook event. Measure before relying on it.
  2. A cd inside the command moves the target. cd /src/x && <checkout> URL wt lands in
    /src/x, not the cwd. This is a common form in this fleet, so a naive "cwd is under
    $HOME ⇒ block every relative target" rule is a serious over-block — the same
    severity as a bypass, because a control that refuses legitimate work gets routed around
    instead of repaired.

Proposed shape (not a specification — needs design)

Fail closed on the residual rather than enumerating spellings:

  • If the command is a checkout and the target resolves under $HOME, block.
  • A target is "under $HOME" when it is an absolute path under $HOME (what #1174 now
    covers) or when it is relative and the effective cwd is under $HOME.
  • The effective cwd is the payload cwd, unless the command changes it, in which case
    the guard has to either resolve the cd or decline to rule.
  • Whichever residual is left must fail closed by construction, and every "declines to rule"
    case must be written down as such rather than being an accident of the regex.

Acceptance

  • Fixtures for all seven shapes above, red against 20d86e39 (otherwise they measure
    nothing).
  • Fixtures pinning the over-block: a checkout after a cd to a legitimate work filesystem
    must still be allowed.
  • Verified by oracle — derive the expected verdict from what the shell actually resolves,
    not from a reading of this issue. The brief has been the defective artifact twice in
    this file's history.

Related

  • PR #1174 (rounds 1–9, the spelling axis)
  • The commit message on 20d86e39 records this gap as deliberately deferred.
## Summary `wrapper-guard.sh` refuses a checkout that lands under `$HOME`. Nine rounds on PR #1174 hardened the rule against every *spelling* of `$HOME` — quoted, braced, escaped, tilde, literal, and every shell word boundary around them. All nine rounds share an assumption that is false: **that the command says where it is going.** A relative target names no directory at all. It resolves against the cwd, and every agent seat on this host runs with a cwd under `$HOME`. ## Measurement Guard at `20d86e39` (current PR #1174 head, i.e. after all nine rounds), cwd `/home/hermes/agent-work`. `want=2` is "must block", `got=0` is "cleared". ``` no target at all (defaults to cwd/<repo>) -> /home/hermes/agent-work/myrepo want=2 got=0 bare relative name -> /home/hermes/agent-work/wt want=2 got=0 explicit ./ -> /home/hermes/agent-work/wt want=2 got=0 the cwd itself -> /home/hermes/agent-work want=2 got=0 relative subdir -> /home/hermes/agent-work/sub/wt want=2 got=0 parent-relative -> /home/hermes/wt want=2 got=0 worktree add, relative -> /home/hermes/agent-work/wt want=2 got=0 7 shapes, 7 uncovered ``` The first row is the one that matters most: a checkout command with **no target argument** is the most ordinary form there is, and it lands in the cwd. ## Why this was not folded into #1174 It is a different rule, not another spelling. Matching home tokens cannot express it — the guard needs the **effective cwd**, and: 1. The cwd available to a PreToolUse hook is the *session* cwd. Whether that field is delivered on PreToolUse specifically is **unverified**; `qa/reflect-stop-hook.sh` consumes `.cwd // empty` with a `${payload_cwd:-$PWD}` fallback, which shows the field exists for at least one hook event. Measure before relying on it. 2. A `cd` inside the command moves the target. `cd /src/x && <checkout> URL wt` lands in `/src/x`, not the cwd. This is a common form in this fleet, so a naive "cwd is under `$HOME` ⇒ block every relative target" rule is a serious **over-block** — the same severity as a bypass, because a control that refuses legitimate work gets routed around instead of repaired. ## Proposed shape (not a specification — needs design) Fail closed on the residual rather than enumerating spellings: - If the command is a checkout **and** the target resolves under `$HOME`, block. - A target is "under `$HOME`" when it is an absolute path under `$HOME` (what #1174 now covers) **or** when it is relative and the effective cwd is under `$HOME`. - The effective cwd is the payload cwd, **unless** the command changes it, in which case the guard has to either resolve the `cd` or decline to rule. - Whichever residual is left must fail closed by construction, and every "declines to rule" case must be written down as such rather than being an accident of the regex. ## Acceptance - Fixtures for all seven shapes above, **red** against `20d86e39` (otherwise they measure nothing). - Fixtures pinning the over-block: a checkout after a `cd` to a legitimate work filesystem must still be allowed. - Verified by oracle — derive the expected verdict from what the shell actually resolves, not from a reading of this issue. The brief has been the defective artifact twice in this file's history. ## Related - PR #1174 (rounds 1–9, the spelling axis) - The commit message on `20d86e39` records this gap as deliberately deferred.
Mos added the bug label 2026-08-13 10:09:12 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1197