ci: add Plumber workflow security check by Totara-thib · Pull Request #936 · air-verse/air · GitHub
Skip to content

ci: add Plumber workflow security check - #936

Open
Totara-thib wants to merge 4 commits into
air-verse:masterfrom
Totara-thib:plumber-ci
Open

Totara-thib wants to merge 4 commits into
air-verse:masterfrom
Totara-thib:plumber-ci

Conversation

@Totara-thib

@Totara-thib Totara-thib commented Aug 4, 2026 •

Copy link
Copy Markdown

Companion to #935, merge that one first: this branch sits on top of it and shrinks to one commit once the hardening lands.

This adds Plumber to CI, the tool that found those issues. It scans the workflows on each push to master and each PR and fails under 85 points when something regresses: a floating action ref, a job without a permissions block, a known CVE.

  • plumber.yml: minimal permissions, its two actions in actions.lock, findings in the security tab as SARIF.
  • .plumber.yaml: a short overlay on the built-in defaults, trusts the smoke test's assert action, branch protection check off while master has no rules.
  • The score badge in the four READMEs.

Score badge

Like OpenSSF Scorecard's published results: every run publishes the score, the badge shows master, a failed publish never fails your CI, no token needed. Gray UNKNOWN until the first run. Removing the score-push line is the whole opt-out.

I work on Plumber. If you do not want the tool, say so and I close this PR, the hardening stands on its own.

Without an explicit permissions block, every job gets the repository's
default token scope, which can include write access to contents, and
runs that build pull request code hold that token while executing code
from the PR. Scoping each workflow to what its steps actually use keeps
a compromised dependency or build script from turning into repo access.

Derived from actual token use per workflow:

- build.yml drops to contents: read; only the push_to_docker_latest job
  keeps packages: write, because it logs in to GHCR with GITHUB_TOKEN
  to push the latest image. Docker Hub and Codecov use their own
  secrets, not the token.
- release.yml drops to contents: read; the release job gets
  contents: write, which GoReleaser needs to create the GitHub release
  and upload archives. The Docker Hub push uses its own credentials.
- smoke_test.yml and smoke_test_reuse_job.yml run no step that writes
  through the token, so they drop to contents: read.
- stale.yml already declares its own scoped permissions, unchanged.
The release job restored the shared Go module cache through setup-go
before GoReleaser builds the binaries that ship to users. GitHub Actions
caches can be written by other workflow runs, so a poisoned cache entry
would flow straight into a published release. Downloading modules from
source on the (rare) release builds costs a little time and removes that
path; go.sum still verifies every module either way.
GitHub's workflow dependency lockfile does the job the SHA pins did,
without touching the readability of the workflow files:
.github/workflows/actions.lock holds the commit each action reference
resolves to, transitive actions included, and the runner uses those
commits even if a tag moves later. The file comes from gh actions-lock
(v0.2.0), which also narrowed every reference to the exact release it
locked (actions/checkout@v5.1.0, goreleaser/goreleaser-action@v5.1.0
and so on), so Dependabot keeps bumping versions and its security
alerts keep matching. Re-run the same command after adding or bumping
an action.
A small workflow runs the Plumber CLI on pushes to master and on pull
requests. It checks the GitHub workflows for the classes of findings
the hardening fixed (floating action refs, missing permissions) and
fails under 85 points, so one small finding never blocks a pull
request. The two actions it uses are recorded in actions.lock like the
others. The overlay keeps the built-in defaults, trusts the assert
action the smoke test uses, and turns off the branch protection check
since master has no protection rules, a repository setting no pull
request can change. The four READMEs get the score badge in their
badge row.
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