This is the record of an abcd-managed repository

abcdBetav0.16.2 · 2026-11-06

Development record

A new tag on an old main commit passes release provenance, rolls installs back and freezes updates at the raised mark

issueresolvedminoriss-2610280103491905

A new tag on an old main commit passes release provenance, rolls installs back and freezes updates at the raised mark

Published as security advisory GHSA-xvvw-mwgc-9hc4; the advisory is the public account of this finding.

What

abcd update, the bootstrap in hooks/bootstrap.sh and the ahoy install promotion bind a release's provenance to "the commit this tag names": the Sigstore bundle's source digest must equal the commit the tag resolves to (internal/core/update/provenance.go:159-231, :353-371). Nothing binds the tag's version to the build.

Someone with write access to the repository can create a new tag, such as v99.0.0, on an old main commit and publish a release carrying that commit's genuine binary and checksums.txt. Tag creation is open under .abcd/work/rulesets/release-tags.json, and immutable releases do not stop a new release. The old run's attestation verifies: release.yml@refs/heads/main, a GitHub-hosted runner, and source digest equal to the tagged commit. A bare update or a bootstrap installs the old build. It then raises the newest-release mark to v99.0.0 (internal/surface/cli/update.go:131-140, internal/core/ahoy/release_mark.go:95-111, hooks/bootstrap.sh:598-620). After that, every genuine later release is refused as older than the mark. Bootstrap's refuse_older leaves the hooks and the shell guard inactive until the person names a tag by hand.

Under the branch rulesets, a writer without admin cannot get a signed build of their own code, so this route gives them something they could not otherwise get: a downgrade, without review, to a genuine old release of their choosing (one with published advisories, for example), plus persistence through the mark on every machine that updated. The comment at provenance.go:35-43 names this downgrade and says the tag binding prevents it.

Reproduced by verifying the recorded v0.13.4 bundle under a new lightweight tag pointing at v0.13.4's commit: verify returns the main-branch signer and no error.

Affected versions

Every released version up to and including 0.15.1 that verifies release provenance. Patched versions will be set when the release carrying the fix is cut.

Would it exist without abcd?

No. The tag-to-commit binding, the newest-release mark and the automatic bootstrap install are abcd's own checks and state, and this is a protection abcd states and does not deliver. abcd added it, so abcd fixes it.

Fix

Bind the tag to the release the workflow cut, not only to a commit. For example, have release.yml attest a statement naming the tag (a tag line in the attested checksums.txt, or the predicate's external parameters), and have every client (update, ahoy promotion, bootstrap) refuse when the attested tag differs from the one requested, before the swap and before the mark is raised. Client-side checks only; no repository setting, hook or workflow trigger changes.

Frontmatter

idiss-2610280103491905
typeissue
lifecycleresolved
severityminor