Development record
2026-09-15 — GHSA-4q78-ccfv-f374 (iss-2609012039102770) is closed by OPTION B, ruled by the maintainer at an interactive question: bind the cache to a record the environment does not choose alone. The owned PATH-copy promotion re-verified the cache only against the `binary-meta` beside it, and `CLAUDE_PLUGIN_DATA` is taken from the environment as given, so whoever chose the directory wrote both the bytes and the record that "verified" them — reproduced at v0.7.0 as a one-byte file installed 0755 as `~/.local/bin/abcd` with provenance recorded. Now the bootstrap, the one process holding the harness's real data dir that has just established manifest trust for the cache (an authenticated cache hit, or a fresh download verified against the same-origin manifest), writes `~/.abcd/cache-attestation` — `data_dir`, the manifest-authenticated `binary_sha256`, `cache_trust=manifest`, `attested_at`; 0600, temp-and-rename, beside `path-entry` — and `ahoy install` promotes a cache only when that record names the directory, the co-located record carries the attested hash, and the artefact hashes to it, whichever route (environment or the root's `.data-dir` stamp) named the directory; detection offers the heal on the same predicate. An offline run neither writes nor rewrites the attestation, so a cache provisioned offline waits for a networked session before it reaches PATH, said out loud. Why B: the trust floor moves from a value the environment supplies to a write into the caller's own home, which adr-46 decision 4 already treats as the ownership root, so the attestation grants nothing that authority did not hold and costs `ahoy install` no network (adr-38 stands). Rejected: A (a manifest GET on a disk-only verb, re-fetching what the session already proved); C (a documented residual — weaker than it reads, since a harness honouring a committed settings file's environment block lets a hostile checkout set the variable, and the owned-copy claim is what the hook shims trust); the mechanical partial of cross-checking against the plugin-root binary (breaks dogfood installs whose root binary is a local build); and a terminal rung through the attestation alone (it would heal a dogfood checkout's stable symlink into a release copy). adr-46 is superseded by adr-2609151706587280 (never amended, always superseded; retained because its numbered decisions are cited), spc-35 Design 2 step 1 and Design 3 are revised in place and dated, and brief invariant 12 gains the clause.
2026-09-15 — GHSA-4q78-ccfv-f374 (iss-2609012039102770) is closed by OPTION B, ruled by the maintainer at an interactive question: bind the cache to a record the environment does not choose alone. The owned PATH-copy promotion re-verified the cache only against the binary-meta beside it, and CLAUDE_PLUGIN_DATA is taken from the environment as given, so whoever chose the directory wrote both the bytes and the record that "verified" them — reproduced at v0.7.0 as a one-byte file installed 0755 as ~/.local/bin/abcd with provenance recorded. Now the bootstrap, the one process holding the harness's real data dir that has just established manifest trust for the cache (an authenticated cache hit, or a fresh download verified against the same-origin manifest), writes ~/.abcd/cache-attestation — data_dir, the manifest-authenticated binary_sha256, cache_trust=manifest, attested_at; 0600, temp-and-rename, beside path-entry — and ahoy install promotes a cache only when that record names the directory, the co-located record carries the attested hash, and the artefact hashes to it, whichever route (environment or the root's .data-dir stamp) named the directory; detection offers the heal on the same predicate. An offline run neither writes nor rewrites the attestation, so a cache provisioned offline waits for a networked session before it reaches PATH, said out loud. Why B: the trust floor moves from a value the environment supplies to a write into the caller's own home, which adr-46 decision 4 already treats as the ownership root, so the attestation grants nothing that authority did not hold and costs ahoy install no network (adr-38 stands). Rejected: A (a manifest GET on a disk-only verb, re-fetching what the session already proved); C (a documented residual — weaker than it reads, since a harness honouring a committed settings file's environment block lets a hostile checkout set the variable, and the owned-copy claim is what the hook shims trust); the mechanical partial of cross-checking against the plugin-root binary (breaks dogfood installs whose root binary is a local build); and a terminal rung through the attestation alone (it would heal a dogfood checkout's stable symlink into a release copy). adr-46 is superseded by adr-2609151706587280 (never amended, always superseded; retained because its numbered decisions are cited), spc-35 Design 2 step 1 and Design 3 are revised in place and dated, and brief invariant 12 gains the clause.
Frontmatter
| id | dcn-2609150000000010 |
|---|---|
| type | decision |
| date | 2026-09-15 |
.abcd/work/decisions/dcn-2609150000000010-ghsa-4q78-ccfv-f374-iss-2609012039102770.md
open on GitHub ↗ · commit history ↗
mentions5
- adr-38Implicit checks are disk-only — the network answers only an explicit ask
- adr-46Persisting the hook binary never weakens the verification posture — every promotion re-verifies, and SessionEnd performs no network work
- adr-2609151706587280The cache reaches PATH only through a home-scoped attestation the environment cannot write
- iss-2609012039102770GHSA-4q78-ccfv-f374 (CWE-345, advisory severity low): the owned PATH-copy verification is self-referential under an env-supplied CLAUDE_PLUGIN_DATA. `internal/core/ahoy/data_dir.go:pluginDataDir` is `os.Getenv("CLAUDE_PLUGIN_DATA")` and nothing else; `owned_copy.go:cacheRecordedSHA` reads `binary_sha256` from a `binary-meta` beside the artefact under that same directory; `apply.go:installOwnedEntry` hashes the artefact, compares it to that record, writes the bytes 0755 as the owned PATH entry and records provenance in `~/.abcd/path-entry`. The bootstrap's manifest authentication (`hooks/bootstrap.sh`, cache_trust=manifest, iss-2608210934566228) covers only the cache-to-plugin-root promotion; the cache-to-PATH promotion re-verifies against the co-located record only, which an attacker who writes both satisfies trivially — adr-46 decision 3 states exactly this principle for the bootstrap. Reproduced at v0.7.0: with CLAUDE_PLUGIN_DATA and ABCD_PLUGIN_ROOT pointed at attacker-chosen directories, `ahoy install --yes --adopt` installed a one-byte file as `~/.local/bin/abcd`, recorded its hash and the fake plugin root in path-entry, and `classifyBinTarget` then vouched for it as an owned copy.
- spc-35hook-binary-to-persistent-data-dir