-
Notifications
You must be signed in to change notification settings - Fork 69
Description
Summary
In NewRegistryClient() in internal/infra/registry_client.go, the error check for getRegistryAuthHeader() is inverted, causing auth credentials to be discarded when available and empty credentials to be set when unavailable.
Details
func NewRegistryClient(image string) *RegistryClient {
// ...
user, pass, err := getRegistryAuthHeader(domain)
if err != nil { // err != nil means credentials NOT found
remoteOptions = []remote.Option{
remote.WithAuth(&authn.Basic{Username: user, Password: pass}),
// sets auth with empty user/pass since getRegistryAuthHeader
// returns ("", "", error) on failure
}
} else { // err == nil means credentials ARE available
remoteOptions = []remote.Option{}
// discards valid credentials, uses no auth
}
// ...
}getRegistryAuthHeader() returns (user, pass, nil) on success and ("", "", error) on failure. The branches should be swapped.
Impact
GetLatestDigest() and DigestExists() always query GHCR unauthenticated (since for ghcr.io images, valid credentials are returned with err == nil, which hits the else branch with empty options).
For public images this still works because GHCR serves public manifests without auth, but unauthenticated requests may hit a cached CDN layer that returns stale tag-to-digest mappings. This could cause the freshness check in pullImage() to incorrectly conclude "image is already up to date" when a newer image has been pushed.
Fix
Swap the branches:
user, pass, err := getRegistryAuthHeader(domain)
if err == nil {
remoteOptions = []remote.Option{
remote.WithAuth(&authn.Basic{Username: user, Password: pass}),
}
} else {
remoteOptions = []remote.Option{}
}