Development record
The armed receipt gate is satisfied by a forged PROMOTE reached through a symlinked commit directory. checkReceiptGate (internal/core/lint/lint.go) reads each receipt with fsutil.ReadGuarded on the unresolved path `.abcd/work/reviews/<sha>/<gate>.json`, and holds only receipts_dir inside the repository once links are followed. O_NOFOLLOW refuses a symlinked leaf, but the kernel follows every ancestor, so a committed `.abcd/work/reviews/<sha>` that is a link to a directory outside the tree, holding a well-formed PROMOTE receipt, yields zero receipt_gate findings: the release gate passes on a receipt the tree does not hold. The release-gate manifest read has the same shape: a symlinked `.abcd/development/release-gate` directory hands the gate an out-of-tree manifest whose hash a forged receipt can echo. iss-2609012037127981 was resolved with grounds that the gate is never satisfied by a receipt reached through a link, so that resolution over-claims.
The armed receipt gate is satisfied by a forged PROMOTE reached through a symlinked commit directory. checkReceiptGate (internal/core/lint/lint.go) reads each receipt with fsutil.ReadGuarded on the unresolved path .abcd/work/reviews/<sha>/<gate>.json, and holds only receipts_dir inside the repository once links are followed. O_NOFOLLOW refuses a symlinked leaf, but the kernel follows every ancestor, so a committed .abcd/work/reviews/<sha> that is a link to a directory outside the tree, holding a well-formed PROMOTE receipt, yields zero receipt_gate findings: the release gate passes on a receipt the tree does not hold. The release-gate manifest read has the same shape: a symlinked .abcd/development/release-gate directory hands the gate an out-of-tree manifest whose hash a forged receipt can echo. iss-2609012037127981 was resolved with grounds that the gate is never satisfied by a receipt reached through a link, so that resolution over-claims.
Grounds
- pursued: an armed receipt gate is never satisfied by a receipt or manifest reached through a link at any component of its path; a forged PROMOTE under a symlinked
reviews/<sha>orrelease-gatedirectory yielding zero receipt_gate findings would show it wrong
Frontmatter
| id | iss-2609261016494611 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609261016494611-the-armed-receipt-gate-is-satisfied-by-a.md
open on GitHub ↗ · commit history ↗
mentions2
- iss-2609012037127981Sibling of GHSA-fh9j-8xmg-m33f (CWE-59, CWE-400), found on the sweep and not fixed there: checkReceiptGate (internal/core/lint/lint.go) reads each review receipt under .abcd/work/reviews/<sha>/<gate>.json and the release-gate manifest through a bare os.ReadFile — no O_NOFOLLOW, no O_NONBLOCK, no regular-file check, no byte cap. The reviews store is committed and travels with a clone, so a committed FIFO at a receipt path hangs abcd lint (and make preflight through it), and a committed symlink is judged as if it were a receipt. The fix is the routing through fsutil.ReadGuarded that lint's readingoutstanding.go already uses for the reading family, with a cap for the receipt shape, and a test with a FIFO and a symlinked receipt. Distinct from iss-2609011423385217 (the manifest's stale pinned inputs) and from the record-store sweep records iss-2608301203521317 and iss-2608211914592726, which do not name the reviews store.
- iss-2609261019593167Two lint reads trust a path the repository does not hold, siblings of the receipt gate's symlinked commit directory (iss-2609261016494611), found on its sweep. (1) loadProseBaseline (internal/core/lint/prosecitations.go) reads the prose-citation baseline at the committed config's baseline path with no containment at all: a path that climbs out with '..', or one whose directory is a link out of the tree, is read, and every id the out-of-tree file names stops firing, so prose_citation_resolves passes on an exemption list the tree does not hold. (2) ReadReadingOutstanding (internal/core/lint/readingoutstanding.go) refuses a link at every directory below the issue store and not at the store root itself, so a symlinked store root carries the whole reading walk out of the tree and the outstanding board reports on records the repository does not hold.