You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ci: fix the last two main-red roots — Hypatia startup_failure (#451, this repo only) + scorecard-verify false-red (new) (#514)
Completes the run to green on `main`, after #511 (secret-scanner caller
+ SPDX) and #512 (gitleaks allowlist). Two remaining causes, independent
of each other and of those PRs.
## 1. Hypatia has never run — the caller under-grants (#451)
`hypatia-scan-reusable.yml` declares `security-events: write` (it
uploads SARIF). `hypatia-scan.yml` granted `security-events: **read**`.
A called workflow may never request more than its caller grants. When it
does, GitHub rejects the run at **startup** — before any job exists:
```
completed startup_failure Hypatia Security Scan main push 1s
$ gh run view 29764318317 --log-failed
failed to get run log: log not found # <- there is no log; no job ran
```
Granting `read` did not degrade the scan to a no-upload mode. **It
stopped the scan from existing.**
The reusable carried this comment:
```yaml
continue-on-error: true # callers granting only security-events:read skip gracefully
```
That is a false mental model, and it is load-bearing — it is the reason
the mismatch looked intentional. Nothing skips gracefully in a job that
was never created. Corrected in place.
This is exactly the #451 pattern that startup-fails reusable-workflow
callers estate-wide. **Fixed here on the canon's own caller only** —
deliberately demonstrated on one repo before any propagation is
proposed.
## 2. Seven "fake passes" that were every one of them real (#381)
`registry-verify.yml` runs `build-scorecards.sh --verify`, which
**executes** every pass-row's check. The job installed no tools, and
`ubuntu-latest` ships neither ripgrep nor xmllint:
| rows | mechanism | observed |
|---|---|---|
| `k9-svc/M6` | calls `xmllint` directly | exit **127** |
| 6 × `release-pre-flight/*` | invoke `v1-audit.sh`, which hard-exits 2
without ripgrep, so the greps match nothing | exit **1** |
The verifier reported all seven as **"claimed PASS but the pass is not
real"**. Reproduced locally by stripping `rg` from `PATH` — identical
output.
Measured with the tools present:
```
── verify: 87 grounded pass · 0 broken pass · 0 unrunnable · 0 self-asserted · 0 stale-fail
EXIT=0
```
**All seven claims were true.** The checks had never run. A false-red is
not harmless: it trains maintainers to ignore a gate — the same end
state as the fake-green gates #500 removed, reached from the opposite
direction.
Two changes, since installing the tools alone leaves the trap armed for
the next missing dependency:
- `registry-verify.yml` installs `ripgrep` + `libxml2-utils`.
- `run_verify()` **refuses to judge pass-rows at all** in an incomplete
environment, and reports genuinely unrunnable checks as `unrunnable` —
still failing, since an unverifiable pass must never go green — rather
than as false claims.
### The required-tool list is declared, not inferred
An earlier draft parsed check strings to discover their commands. It
misparsed quoted regexes (`"a\|b\|c"`) and `[ $(…) -ge 25 ]`, inventing
**35** failures — precisely the sin the guard exists to prevent.
Replaced with a measured, documented list: `xmllint` is named by 4
checks, `jq` by 1, and `rg` by none but is required transitively via
`v1-audit.sh`.
## Verification
- complete env → `87 grounded · 0 broken · 0 unrunnable`, exit 0
- incomplete env → fails fast, judges nothing, names the missing tool
- `k9-svc/mime/k9.xml` + `k9.uti.plist` independently confirmed
well-formed and the two companion files present — so installing xmllint
turns that row **genuinely** green rather than exposing a real fault
- all three workflow files parse
## Not included, on purpose
`.gitleaks.toml` — **#512 covers it and is the better implementation.**
I built one independently and then discarded it: my regexes matched
against the whole line unanchored, and canary-testing showed that
suppressed real tokens.
| planted secret | gitleaks defaults | my draft |
|---|---|---|
| `ghp_…` PAT | caught | caught |
| same PAT, line also contains `EXAMPLE_KEY` | caught | **suppressed** |
| same PAT, line ends `# ...` | caught | **suppressed** |
#512's anchored `^…$` design has no such hole. Verified independently
against gitleaks 8.18.4 (the exact CI build, checksum-matched): all four
adversarial fixtures still caught, and `0` findings on the tree. Adopt
#512.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
0 commit comments