Skip to content
oss-kit
GitHubInstall

A tag-published registry entry updates through the forge, not through a token a CI job holds

Rule
R-PUB-07
Forge scope
GitHub and GitLab
Fixed by
oss-publish

The tag-published track has no publish step to secure, but the registry still has to learn that a new version exists. Where that link is a registry API token sitting in CI secrets, the track has reintroduced the exact credential it otherwise avoids: any workflow compromise can then push a package version, without the project ever running a publish command.

Observable check

the registry entry is updated by a forge-side integration the registry itself configures, and no registry API token appears in CI secrets or in any workflow file. On GitHub, Packagist's documented path is authorizing the Packagist application and letting it install the hook, so no token is stored anywhere; the package page shows an auto-update warning when no hook is set, which is the observable evidence. Where the registry offers no such path, which is the case for Packagist on GitLab, the credential belongs in the forge's project integration settings, where no job can read it, and never in a CI variable. An ecosystem that exposes no registry entry to update falls outside this rule rather than failing it, which is the case for Go modules: proxy.golang.org fetches a tag on demand and offers no account, no registration, and no credential.

Where this comes from

Last read against these sources on 2026-07-31.

Packagist's own page carries both halves of the Check. On GitHub the documented path stores no token anywhere: 'Make sure you log in via GitHub' and grant 'the Packagist application' access, after which Packagist installs the hook itself, and the observable failure is the auto-update warning it tells you to look for on the package page. The manual GitHub fallback and every other forge put the API token in the forge instead, and GitLab has no tokenless option at all: GitLab's own integration model requires a username and a token typed as a password. That asymmetry is why the rule asks where the credential lives rather than whether one exists, which is the distinction that matters, because a token in a project integration setting is not readable by a CI job and a token in a CI variable is. The Go clause is a reading of an absence and the sources are what makes it one: neither the publishing guide nor the proxy page offers an account, a registration, or a credential, and the proxy is explicitly demand-fetched. Retire the GitLab half the day Packagist ships an application authorization there, and retire the rule outright if the tag-published track ever gains a publish step.

Read the whole standard