Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ repository still gets a decision, never by following the link; no release carrie
- :memo: docs(openspec): amend AUD2-S03 to the shipped pin and log D-153 (SEC-03)
- :memo: docs(audit): document the AUD2 gate and correct stale line counts (F4, F6)
- :memo: docs(backlog): mark P5-AUD2 complete (S01-S05 landed)
- :memo: docs(e10): GitHub addressing & representation model (E10-S00)
- :memo: docs(e10): demote GitHub eligible-approval-evidence to unknown (review fixes)

### Features
- :sparkles: feat(audit): measure REL-03/REL-07 by real mutation run, not by name
Expand Down
1 change: 1 addition & 0 deletions docs/decisions/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ project/process decisions.
| D-151 | 2026-08-16 | **OpenSSF Best Practices project creation is RESOLVED: (a) — the operator will create the `bestpractices.dev` project for `PlatformRelay/assent`.** Unblocks SEC-SC-S02 (evidence page + README badge, once *passing*); SEC-SC-S01 (fuzzing) is not blocked by this and can proceed independently. Operator action only — requires a personal account at bestpractices.dev, not automatable in-tree. Current CII score is 0, which drags the OpenSSF Scorecard (~7); the repo already satisfies most criteria (pinned CodeQL, Dependabot, secret scanning, signed releases, SLSA provenance). Revert: leave CII at 0 indefinitely — SEC-SC-S01 is unaffected either way. |
| D-152 | 2026-08-18 | **The 2026-08-18 audit's "Next (risk reduction)" wave is decomposed as its own epic P5-AUD2 (`openspec/specs/p5-aud2-audit-remediation/spec.md`), five stories, spec-first.** Context: `agent-context/PROJECT-AUDIT-2026-08-18.md` closed both P1 conditions the same day and v0.3.0 shipped, leaving a named but untracked wave — exec-transport trio (REL-01/02/07), REL-03 `ErrNotFound` discrimination, SEC-03 cosign identity pin, TEST-02 mutant. Options considered: **(a)** hand-fix them as loose commits (rejected — AGENTS.md rule 4 is spec-first, and REL-01 is now byte-identical across *three* audits precisely because nothing ever tracked it); **(b)** append them to the existing P5-AUD epic (rejected — that epic's exit gate is CLOSED and reopening it would make "AUD complete" a moving claim); **(c)** a new AUD2 epic keyed to this audit **(chosen)**. Three consequences recorded here so they are not re-litigated: **(1)** AUD2 has **no release-condition story** — the audit had exactly two P1s and both are already closed; **(2)** **WG-S01 is deliberately excluded** — it carries the LGTM governance marker, and GOVERNANCE says such stories are surfaced to the maintainer, which an autonomous loop's decide-and-log does not override; **(3)** AUD2's exit gate is a **`task check` stage**, not a `release-exitgate` step, because that job is `pull_request`-skipped (RELSE-08) and wiring the gate there is how AUD-S18's own stale `CHECK_STAGES` pin survived four merges. A 2026-08-10-keyed AUD2 draft exists only in the local stash `leave-aud2-not-this-epic`, was never committed, and is superseded; its still-open items (F3/F5/F7) stay Later-wave. Revert: delete the spec + backlog section; the four findings return to the audit report untracked. |
| D-153 | 2026-08-19 | **The cosign signer-identity pin published in `SECURITY.md` was WRONG, not merely missing from `hack/install.sh`: the identity regexp is widened to `^https://github\.com/PlatformRelay/[Aa]ssent/` in both files (AUD2-S03 / SEC-03).** Substance first: `SECURITY.md`'s "Verify a tagged release" instructions pinned `--certificate-identity-regexp '^https://github.com/PlatformRelay/assent/'`, and an adopter who followed them on **v0.2.0 or v0.3.0 got a verification FAILURE on a genuine, correctly signed artifact** — the published recipe has been broken for every release since v0.2.0, and the natural reading of that failure is "this release was tampered with". Cause: the repository was renamed `PlatformRelay/assent` → `PlatformRelay/Assent` between v0.1.0 and v0.2.0; the keyless signing certificate's Subject Alternative Name carries GitHub's canonical casing; cosign compiles `--certificate-identity-regexp` as a Go RE2 pattern and matches it **case-sensitively**. Decoded from the published bundles: v0.3.0 and v0.2.0 sign as `https://github.com/PlatformRelay/Assent/.github/workflows/release.yaml@refs/tags/vX.Y.Z`, v0.1.0 as `https://github.com/PlatformRelay/assent/.github/workflows/release.yaml@refs/heads/main`. All three verify under the new value; independently re-confirmed with real cosign against the real v0.3.0 artifact (`Verified OK` under the new pin, exit 1 "expected SAN value to match regex" under the old). Options considered: **(a)** ship the pin byte-identical to the published-but-broken value and file the breakage as a follow-up (rejected — it would land a green gate asserting a guarantee that fails closed on this project's own releases, D-124's defect one level up); **(b)** case-insensitive `(?i)` (rejected — wider than the defect and it silently accepts casings GitHub never issues); **(c)** an explicit `[Aa]` class in both files, dots escaped (chosen). Not widened otherwise: the `^` anchor and the owner/repo scope stand, so another owner, an `assent-mirror` typosquat, another forge and an unescaped-dot host all still fail. Enforcement, per D-128: **one** published truth — `hack/release/install_cosign_pin_test.sh` extracts the pair from `hack/install.sh` and `SECURITY.md`, requires exactly one distinct value per file, reddens on drift, and (this is the assertion that would have caught the defect) matches the pin against the three **real** SANs above, committed as offline fixtures, plus six negatives. Extends D-110 (bundles beside archives) and D-109; the maintainer-path twin at `hack/release/verify-artifacts.sh:124` is still unpinned and is tracked in the backlog, not fixed here. Revert: restore `'^https://github.com/PlatformRelay/assent/'` in both files — which re-breaks verification of v0.2.0 and every later release, so revert only together with a repo rename back to lowercase. |
| D-155 | 2026-08-23 | **E10-S00 — the GitHub addressing & representation model is written ([docs/planning/github-addressing-model.md](../planning/github-addressing-model.md)) and ADR-0021 items 5–8 are executed UNAMENDED.** **(1) Fork-head addressing (P0):** the governed subject moves to MR-relative `FileAtBase(mr, path)` / `FileAtHead(mr, path)`; `FileAtRef(project, path, targetRef)` survives for ref-addressed decision inputs. Verified against the tree rather than transcribed: **exactly two call sites migrate** (`cmd/assent/run.go:270`, `:274`) and **six must not** (`run.go:203/211/230/249`, `provider_host.go:82/292`). **Anchor drift, attributed to the document that carries it:** ADR-0021's genuine drifts are **two** — `provider_host.go:275` (the D-130 who-may-approve registry read; actual `:292`) and the proposed `Describe(project, mr)` (the tree's method is `GetMR`). A third stale anchor, `provider_host.go:246 refFilePort` (actual `:263`), lives in `openspec/specs/p5-e10-github-forge/spec.md:257` — E10-S02's own DoD, where an implementer would have tripped on it — and is **fixed in this change**. **(2) Capability predicates:** every dossier §4 flag gets one of two kinds — `P` probed (named endpoint + field + comparison) or `C` contract-proven (a constant licensed by a NAMED conformance case; no case ⇒ `unknown`). Counted against the tree, GitLab probes 3 of 11 honestly, 1 by heuristic, hardcodes 1, and leaves **7 implicitly assumed**; `unknown ⇒ never arm` binds per capability at its consultation point (`internal/forge/precondition.go` consults 3 today), not "all eleven". **BOTH arming-relevant GitHub capabilities are `unknown`, so v1 GitHub COMMENTS AND DOES NOT GATE** — the fail-closed outcome, stated up front. `protected-pipeline-source` is `unknown` on **both** forges (GitLab's `strings.Contains(ci_config_path, "@")` is a heuristic, not a predicate) → **OQ-33**, which now poses ADR-0015 §4's own three routes including the largely forge-readable same-repo one; retiring the heuristic is a user-visible GitLab behaviour change owed its own D-row at S04/S09 (judgment call (e)), not minted here. `eligible-approval-evidence` is **`unknown`** → **OQ-34**: the CODEOWNERS route is legitimate (ADR-0017 §3 names it; the frozen schema enumerates `codeowners`), but no probeable conjunct decides what `full` DENOTES — that the adapter-computed eligible set EQUALS the forge's — and an over-permissive matcher would make the DecisionRecord assert an obligation satisfied by an ineligible principal, harm needing no arming. This **confirms** ADR-0021's Consequences prediction (whose stated reason was dossier §2 step (b), the CODEOWNERS step) and **agrees with** the dossier's own `partial` verdict; an earlier draft of this model graded it `full`, contradicting that spec input, and the wrong answer is kept visible in the doc rather than edited away. **(3) Record surface (P0):** ADR-0021 item 8 option (ii) confirmed — the multi-capability report is `doctor` output and arming-refusal reasons only, never the DecisionRecord; **`git diff schemas/ == 0` holds and NO schema change is proposed.** There is **no gap-selection rule to invent** — the field is reserved for one capability. Stated honestly rather than over-claimed: the `allOf` forbids `capabilityGap` only when `mergeResultDigest` is pinned, and `run.go:368` sets the gap **unconditionally**, so the channel is open in 100% of runs today and the schema clause is a **latent** guard until an adapter pins a real digest (S03/S07); what closes the comma-join loophole meanwhile is a conformance case, not the schema. The audit-trail cost is real but **pre-existing**: `aggregate.Result.CapabilityGaps` already never reaches the record. **(4) Status → sentinel:** `forge.ErrUnauthorized` lifts to the port at S02; a GitHub **404 is absence only when a CONTENT-SCOPE probe (a sibling `/contents` read at the same ref) returned 200** — deliberately NOT a repo-metadata probe, since a fine-grained PAT with `metadata: read` and no `contents: read` gets 200 on `GET /repos/{o}/{r}` and 404 on every file, which would re-open the P0 through the front door; a 404 on branch-protection/ruleset reads without `permissions.admin` is `unknown` (never `absent`), and rate-limited 403s are transport errors — extending the AUD2-S02/REL-03 discrimination one layer down into the adapter that mints the sentinel. **Twelve** conformance case IDs are minted, each fail-closed case paired with a positive control (E10-S12's rule applied early), and they are **wired into S01/S07/S14's DoD in the epic spec** rather than left in a planning doc nothing reads; `internal/forge/conformance/catalog.yaml` remains S01's to edit. ADR-0021 stays **Proposed** — accepting it is the maintainer's call, and E10-S00 is `[LGTM]`. |
Loading
Loading