Development record
The 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.
The 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.
Grounds
- pursued: refusing a marker root the caller does not own closes the plant because an attacker who can write a shared ancestor is by construction not its owner, while a home-scoped declaration re-admits a legitimate foreign-uid checkout without the tree being able to assert it; it would be shown wrong by a route through which a refused tree still governs a session, by a declaration the tree can cause to be written, or by a legitimate foreign-uid setup (a CI runner or container whose home is not the caller's) for which no caller-controlled declaration is reachable.
Frontmatter
| id | iss-2609020259564193 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609020259564193-the-rules-root-fallback-still-accepts-a.md
open on GitHub ↗ · commit history ↗
mentions1
- iss-2609020219198779The 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.