diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 1f61bfb..16fe56a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -40,6 +40,12 @@ jobs: with: results_file: results.sarif results_format: sarif + # Fine-grained PAT (repo-scoped, Administration: Read-only) lets the + # Branch-Protection check read classic protection rules the default + # GITHUB_TOKEN can't see. The `|| github.token` fallback keeps the scan + # working where the secret is absent (forks / template copies) — only + # Branch-Protection degrades there. See README "Code scoring and attestation". + repo_token: ${{ secrets.SCORECARD_TOKEN || github.token }} # Public repo: publish to the OpenSSF API so the score is queryable # (and a badge can be added to the README if wanted). publish_results: true diff --git a/README.md b/README.md index 0066171..0720ec1 100644 --- a/README.md +++ b/README.md @@ -1585,6 +1585,8 @@ Automated scoring/scanning of the repo, with badges in the row at the top of thi These are reference-architecture niceties, not gates — the enforced quality bars remain the CI jobs, the cdk-nag packs, and the 100% unit-coverage gate documented above. +**Branch-Protection and the optional `SCORECARD_TOKEN`.** Scorecard's [Branch-Protection](https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection) check reads the repository's protection rules to score them — but the default `GITHUB_TOKEN` cannot read *classic* branch-protection settings, so on that token the check reports an internal error and scores `-1`. That's a token-visibility limit, **not** a sign the branch is unprotected (`main`'s protection is configured regardless). To let the check actually read and score the rules, `scorecard.yml` passes an optional fine-grained PAT via `repo_token: ${{ secrets.SCORECARD_TOKEN || github.token }}` — a token scoped to this repository with a single `Administration: Read-only` permission (see the [scorecard-action auth guide](https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md)), stored as the `SCORECARD_TOKEN` repository secret. The `|| github.token` fallback is deliberate: repository secrets are never copied to forks or "Use this template" copies, so where `SCORECARD_TOKEN` is absent the scan falls back to the ephemeral token and still runs cleanly — only Branch-Protection degrades to `-1` there. A fork or template copy that wants the check to score creates its own `SCORECARD_TOKEN` the same way; nothing about the upstream token carries over. + **Why coverage is kept in-house (Codecov not used).** [Codecov](https://github.com/codecov/codecov-action) is the only one of these that's a genuine third-party SaaS: the uploader sends the coverage report off to codecov.io, and Codecov pulls the source from GitHub (via a granted app/token) to render it — a new external integration and access surface, in a repo whose whole identity is keeping the build inside GitHub with pinned, hash-verified inputs (Codecov's own uploader was the vector in the 2021 supply-chain breach). The marginal gain over what's already here — the CI coverage artifact, the enforced 100% `lambda/` gate, and `make coverage`'s combined HTML report — is hosted history and PR-diff comments, which didn't justify the third party. So the coverage **badge** is wired from our own data instead: `make coverage-badge` runs the same combined cross-venv coverage as `make coverage`, writes a shields endpoint JSON, and the docs workflow publishes it to Pages. (First render waits for the next `main` push to deploy the JSON.) **Considered and not pursued: [SLSA](https://github.com/slsa-framework/slsa) build provenance.** SLSA provenance exists so a *consumer* can verify an artifact before running it. This repo is a "Use this template" reference, not a published package or binary — forks run `cdk deploy`, which rebuilds the Lambda bundle from source, so nothing actually consumes a released artifact. Wiring it (via [slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator)) was prototyped and removed: it required attesting a synthetic bundle that nothing deploys, and it forced the only non-SHA-pinned action in the repo (the generator self-identifies from its ref and rejects SHA pins), which both muddied the "every action is SHA-pinned" posture and docks the Scorecard score — real cost for what would only ever be a worked example here. A fork that genuinely *publishes* a consumed artifact (a shared Lambda layer, a wheel, a container image) should add it then, when there's a consumer to protect.