Skip to content

fix(ci): grant the secret-scanner reusable its required job permissions - #124

Merged
hyperpolymath merged 3 commits into
mainfrom
fix/secret-scanner-permissions-signed
Jul 21, 2026
Merged

fix(ci): grant the secret-scanner reusable its required job permissions#124
hyperpolymath merged 3 commits into
mainfrom
fix/secret-scanner-permissions-signed

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Every Secret Scanner run in this repo has been ending in startup_failure — meaning secret scanning has never actually executed here.

Root cause

A called reusable workflow may only request permissions equal to or more restrictive than its caller. This is enforced when the workflow file is parsed, before a runner is allocated — which is why it surfaces as startup_failure with no logs and no annotations.

secret-scanner-reusable.yml declares this on its gitleaks job:

permissions:
  contents: read
  pull-requests: write   # gitleaks-action posts the PR summary comment
  actions: read          # workflow-run metadata / PR-files endpoint

This caller granted only the file-level permissions: contents: read, so the reusable was asking for more than the caller had, and GitHub refused the run outright.

Evidence

Two repos pinning the identical reusable SHA behave differently based only on this block: modshells (grants the superset) runs fine; verisimdb (does not) startup_failures. Estate-wide, 176 callers grant it and run; 26 did not and were all dead.

Fix

Grant the superset at job level — byte-identical to the canonical template. No SHA pin is changed, so the scanned content and supply-chain posture are unaffected; this only lets the existing pinned scanner start.

Verified

On hyperpolymath/vext, this exact change moved Secret Scanner from startup_failure to success, with gitleaks, rust-secrets and shell-secrets all allocating runners and passing.

🤖 Generated with Claude Code

The scan job calls secret-scanner-reusable.yml, whose gitleaks job declares
pull-requests: write (PR summary comment) and actions: read (workflow-run
metadata) at job level. A called reusable workflow may only request
permissions equal to or more restrictive than its caller, and this caller
granted only the file-level contents: read — so GitHub refused the run at
parse time. Every Secret Scanner run ended in startup_failure, meaning
secret scanning has never actually executed in this repo.

Grants the superset at job level, matching the canonical template and the
176 estate repos whose scanner already runs. No SHA pin is changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hyperpolymath and others added 2 commits July 21, 2026 06:33
secret-scanner-reusable.yml deliberately retired TruffleHog as redundant
("gitleaks provides sufficient coverage at lower cost" — see the reusable's
header). The inline job survived here, fails, and turns the whole workflow red
even when gitleaks, rust-secrets and shell-secrets all pass — so the repo keeps
emitting ci_activity notifications after the startup_failure is fixed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
@hyperpolymath
hyperpolymath merged commit 627fa31 into main Jul 21, 2026
11 of 14 checks passed
@hyperpolymath
hyperpolymath deleted the fix/secret-scanner-permissions-signed branch July 21, 2026 19:15
hyperpolymath added a commit that referenced this pull request Jul 27, 2026
…#129)

Two workflows in this repo are rejected by Actions **at parse time**,
before a runner is allocated. Neither has ever produced a result. Two
independent faults, one per file.

## 1. `secret-scanner.yml` — `jobs:` was empty

The job key `scan:` sat at **column 0** — a sibling of `jobs:`, not a
child:

```yaml
jobs:
scan:            # <-- column 0
    permissions:
      ...
```

So `jobs:` parsed as `null` and Actions refused the file.

This is **valid YAML but invalid Actions**, which is why linting never
caught it — a `yaml.safe_load` sweep over all 22 workflows here passes
this file happily. Only Actions' own schema rejects it.

**Consequence:** the job-level `permissions:` block added in #124 is
attached to a top-level key Actions ignores. That fix has been inert,
and **gitleaks has never executed in this repository.** The diagnosis in
#124 was right and the reusable SHA pin is correct and untouched — the
fix simply never took effect.

Fix: indent `scan:` by two spaces. The child keys were already at 4, so
the block nests correctly.

## 2. `lean-proofs.yml` — unquoted `": "` in three step names

Parse error at line 60, column 63: `mapping values are not allowed
here`. An unquoted YAML scalar cannot contain `": "`, and three `-
name:` values do:

```
(§7.1 not-regular: DFA + pigeonhole)
(§7.3 CFL closure: union/concat/star)
(§7.3 non-closure: pumping foundation)
```

Fix: quote all three.

**This one inverts the usual failure mode.** `lean-proofs.yml` is an
*honest* gate — it installs the pinned Lean toolchain and runs `lean
<file>` with no `|| true`, and was written specifically to stop the
bit-rot recorded in `docs/proofs/verification/AUDIT.md`. It is not a
fake gate that passes when it shouldn't. It is a **real gate that has
never run.**

The proofs behind it are green, verified locally before this PR:

| | prover | result |
|---|---|---|
| 6 × `.lean` | Lean 4.30.0 (per `lean-toolchain`) | all PASS |
| 3 × `.v` | Coq 8.20.1 | all PASS |

Trust base is clean: **zero `sorry`, `Admitted`, `axiom` or
`Parameter`.** So nothing had rotted — the guard was simply switched
off.

## Verification

All 22 workflow files now pass a structural check mirroring what Actions
requires: parses as a mapping, has a trigger, and `jobs` is a non-empty
mapping in which every job has `steps` or `uses`.

Expect this PR itself to demonstrate the fix: `lean-proofs` triggers on
changes to `.github/workflows/lean-proofs.yml`, so it should run here
for the first time. **`Secret Scanner` will also run for the first time
— expect findings that need triage rather than a clean pass.**

## Recognising this class

Both faults share one signature, worth knowing:

- the run is listed by **file path** instead of workflow name
- `gh run view --log-failed` returns **"log not found"**
- `gh pr checks` shows **nothing at all** — a parse-rejected workflow
creates no check run

Only `gh run list --json conclusion` reveals them.

Estate note: the col-0 `jobs:` fault is **not** widespread. A scan of
all 13,330 workflow files across `hyper-repos`/`meta-repos`/`repos`
found it in exactly two — this repo and
`voyage-enterprise-decision-system`, byte-identical in shape. That one
is being fixed in the same pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant