Development record
The rules-root bound from GHSA-vvqc-3mv2-5p49 does not close the user-scope ~/.abcd when the home directory is itself a git working tree (dotfiles-in-home). ResolveRoot bounds the walk at the toplevel, and for a session in a non-repo directory beneath a version-controlled home that toplevel IS the home, so the home-scope rules.json and guard.json still govern the session: injected rules, the kill switch and the guard registry all come from a file outside any project. Cross-UID /tmp is NOT closed either, contrary to what this record first said: git refuses on ownership and the .git-marker fallback then resolves the planted tree's OWN root, which is precisely what a foreign-uid `git init` in a shared directory wants — that residual is recorded separately as iss-2609020259564193 and needs an ownership policy. What is closed is the one-command plant: a bare `.git` file or an empty `.git` directory no longer bounds the fallback, because the marker must now look like a repository (a `.git` directory carrying HEAD, or a `.git` file beginning "gitdir: "). A version-controlled home and a foreign-uid repository both remain open. Evidence: ResolveRoot in internal/core/rules/root.go. The fix needs a decision first — whether a home-directory toplevel is a legitimate configuration scope (spc-23 plans a user layer that would make it one) or must be excluded outright — so nothing is changed here; the doc comment now states the exact scope and names this record.
The rules-root bound from GHSA-vvqc-3mv2-5p49 does not close the user-scope ~/.abcd when the home directory is itself a git working tree (dotfiles-in-home). ResolveRoot bounds the walk at the toplevel, and for a session in a non-repo directory beneath a version-controlled home that toplevel IS the home, so the home-scope rules.json and guard.json still govern the session: injected rules, the kill switch and the guard registry all come from a file outside any project. Cross-UID /tmp is NOT closed either, contrary to what this record first said: git refuses on ownership and the .git-marker fallback then resolves the planted tree's OWN root, which is precisely what a foreign-uid git init in a shared directory wants — that residual is recorded separately as iss-2609020259564193 and needs an ownership policy. What is closed is the one-command plant: a bare .git file or an empty .git directory no longer bounds the fallback, because the marker must now look like a repository (a .git directory carrying HEAD, or a .git file beginning "gitdir: "). A version-controlled home and a foreign-uid repository both remain open. Evidence: ResolveRoot in internal/core/rules/root.go. The fix needs a decision first — whether a home-directory toplevel is a legitimate configuration scope (spc-23 plans a user layer that would make it one) or must be excluded outright — so nothing is changed here; the doc comment now states the exact scope and names this record.
Grounds
- pursued: a session in a plain directory beneath a version-controlled HOME resolves to that directory, not the home, and the home's guard.json does not switch its guard off; a Resolve that returns the home (or a walk that stops at ~/.abcd from beneath it) would show it wrong
Frontmatter
| id | iss-2609020219198779 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-2609020219198779-the-rules-root-bound-from-ghsa-vvqc-3mv2.md
open on GitHub ↗ · commit history ↗
mentions3
- dcn-26092600000000062026-09-26 — Three of the rules loader's security records close, and one stays owed to the product thinker (autonomous run A orchestrator's lane brief, taken by the implementer of lane drainS2). (1) The home directory is never a session's repo root (iss-2609020219198779, answering the owed question "is a home-directory git toplevel a legitimate config scope, or excluded outright?" as the brief rules it): its `.abcd/` is the user layer, so the root walk passes over the home and a toplevel that is the home resolves like a non-repo directory. The lane narrowed the brief's "or an ancestor of HOME": a toplevel that contains the home, the shape of a hermetic harness that points `HOME` inside its checkout, stays the root because git vouched for it and its own `.abcd/` is its own; only the stop at the home is removed. A session whose working directory is the home still reads a `.abcd/` there as the working directory's, the posture question recorded on 2026-09-25. (2) A bundled guardrail that an override withholds is named on every load (iss-174): for COMMITTING, LOAD and PII, each bundled recall keyword, alias or rule missing from a list an override set goes to stderr with the file whose list is in force, and the merge stays per field. The other bundled domains are left out because a repository restates them in its own words, and a note on every restatement would bury the one that matters. Still owed to the product thinker: whether security-bearing lists should union with the bundled entries or take a replace-versus-extend marker instead (itd-117's finer-grained-merging follow-up). (3) The foreign-uid refusal says what it still reads (iss-2609251522588539): the note, the configuration chapter and the install how-to now say that a `.abcd/` at the working directory is read, as AGENTS.md has since 0434d475. (4) iss-2609020219265817 is deferred past v0.11.0, not closed. Every CommonMark heading construct in a rule body (ATX on any line, the first line included, setext, and HTML h1-h6) can be closed only by a code-safe rendering that flattens legitimate structure, or by a fence-aware escaper that is complete only by enumeration and changes the raw text the model reads. So "escaped, fenced, or left to the line-start contract" is the product thinker's ruling.
- iss-2609020259564193The rules-root fallback still accepts a foreign-uid repository planted in a shared ancestor. ResolveRoot falls back to gitutil.RepoShapedRoot when git will not answer, and now requires that marker to look like a repository (a .git directory carrying HEAD, or a .git file beginning "gitdir: "), which closes the one-command plant but NOT a real repository: 'git init' in a shared temp directory owned by another uid makes git refuse on ownership under abcd's isolated environment (GIT_CONFIG_GLOBAL=/dev/null discards safe.directory), and that refusal is the SAME signal in the attack and in the motivating case a legitimate foreign-uid checkout or container bind mount presents. A session whose working directory is a plain directory beneath such a plant therefore resolves the planted tree as its root and reads that tree's .abcd/rules.json and .abcd/guard.json: injected rules, the loader kill switch and the hazard registry all come from a directory anyone can write. Evidence: ResolveRoot and plausibleRepository in internal/core/rules/root.go. The fix needs an ownership policy decided first (the run takes no design decisions): whether the fallback must refuse a marker root whose owner is not the caller, and what a shared CI checkout, a container bind mount and a multi-user host then do, or whether the git-refused fallback should be narrowed to a root the caller owns and fail closed otherwise. Nothing is changed here; the resolver's doc comment states the residual and names this record.
- spc-23development-instructions-live-in-the-right-tier-abcd-manages