Require a signed tag, and show the badge that was earned - #47
Merged
Conversation
The provenance attestation answers a narrower question than it appears to. It proves what built an artifact -- this workflow, this repository, this commit -- and nothing about who authorised the release. Anyone able to push a tag starts the workflow, and the attestation on the result is perfectly valid. For a one-maintainer project the realistic supply-chain attack is a stolen GitHub account, and until now every link in the chain lived inside GitHub. A signing key does not. The release workflow now verifies the tag's signature against .github/allowed_signers before it builds anything, so GitHub access alone is no longer enough to publish. This cost almost nothing to add because the key already existed: commits are already SSH-signed with it, so tag.gpgSign was the only setting missing and no second key has to be managed. Verified in both directions before committing. A signed tag reports a good signature for the expected key; an unsigned tag is refused. The workflow step reproduces that check with an explicit error naming the file to fix. RELEASING.md documents the two things a reader should know before relying on it: a lost key blocks releases until a new public key is committed, and an attacker holding the GitHub account could still open a pull request removing the check -- which makes it a multi-step attack recorded in git history rather than a silent tag push, not an impossibility. Also adds the OpenSSF Best Practices badge to the README. The project reached the passing level at 100%, with three criteria deliberately recorded as unmet rather than claimed: no coverage metric is published, and no dynamic analysis is performed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The gap this closes
The provenance attestation answers a narrower question than it appears to. It proves what built
an artifact — this workflow, this repository, this commit. It proves nothing about who authorised
the release: anyone able to push a tag starts the workflow, and the attestation on the result
would be perfectly valid.
For a one-maintainer project the realistic supply-chain attack is a stolen GitHub account. Until
now, every link in the chain lived inside GitHub — branch protection, the environment gate, the
attestation. A signing key does not.
The release workflow now verifies the tag's SSH signature against
.github/allowed_signersbeforeit builds anything.
Why it was nearly free
The key already existed. Commits in this repository are already SSH-signed with
~/.ssh/id_ed25519.pub; onlytag.gpgSignwas unset. No GPG, no keyring, no second key to manage.Verified in both directions, before committing
The workflow step reproduces exactly that check and fails with an error naming the file to fix.
What
RELEASING.mdnow says plainlyTwo things a reader should know before relying on this:
.github/allowed_signers.The control is only as available as the key.
verification step and merge it. Signing makes that a multi-step attack recorded in git history
rather than a single silent tag push. That is the realistic protection available, and claiming
more would be false.
Also: the OpenSSF Best Practices badge
The project reached passing at 100% (project 13969).
Three criteria are deliberately recorded as unmet with justification rather than ticked: no
code-coverage metric is published, and no dynamic analysis or fuzzing is performed. Several others
are honest N/A — the project stores no passwords, generates no keys, and is written in a
memory-safe language.
Sequencing
This must merge before
v0.1.0is tagged. Once merged,git tag -a v0.1.0signs automatically(the local config is set) and the workflow will verify it. Tagging first would produce an unsigned
tag that this check then rejects.
Verification
229 tests green.
release.ymlparses as valid YAML with the new step second in thebuildjob,correctly gated on
startsWith(github.ref, 'refs/tags/v')so manual rehearsal runs are unaffected.All relative links resolve.