`helm pull --verify` fails on a correctly signed chart when the repository name differs from the chart name
- Dominant language
- Go
- Stars
- 30.2k
- Forks
- 7.8k
- Avg merge
- 20h 58m
- Merged PRs (30d)
- 36
Description
### What happened?
A chart signed at package time cannot be verified when it is pulled from a repository named after something else:
```console
$ helm pull oci://localhost:5099/charts/foo:0.1.0 --plain-http --verify --keyring "$GNUPGHOME/pubring.gpg"
Pulled: localhost:5099/charts/foo:0.1.0
Digest: sha256:278e1cd2439aee3d07a559f29921859d355fe03cd6f304a45babbd05c19e020c
Error: provenance does not contain a SHA for a file named "foo-0.1.0.tgz"
```
The same chart, the same signature, pulled from the repository named after the chart, verifies:
```console
$ helm pull oci://localhost:5099/charts/bar:0.1.0 --plain-http --verify --keyring "$GNUPGHOME/pubring.gpg"
Signed by: Helm Repro
Using Key With Fingerprint: D99616A6EF85B14F28E76DE19E0766A448857C16
Chart Hash Verified: sha256:ecd61ca508db32cb765021964651a22eec5ce33dbefe8c8eb934853f1b25edd1
```
Nothing about the chart differs between the two runs. What differs is the name the archive is checked under. `pkg/downloader/chart_downloader.go:156-159` builds that name from the reference, `filepath.Base(u.Path)` plus the tag, and `:311-320` derives it the same way for verification. `pkg/provenance/sign.go:266` then looks the archive up in the signed manifest by that name. The manifest was written by `helm package` and names the chart, so the lookup asks for a file it does not describe. The comment above the verification code already records the assumption this rests on.
`helm pull` without `--verify` handles the mismatch fine, so nothing else in the pull notices it. A repository can also hold several charts under one index, and only one of them can be named after the repository.
#32529 reports the same mismatch from another side, `helm dependency update` followed by `helm lint`, and HIP helm/community#443 proposes a way to express both identities for dependencies. Both are about dependency declarations and neither covers this path. Verification needs no new field and no change in semantics; it needs the name the archive is checked under to be the name the signature was made over.
### What did you expect to happen?
A chart that was signed and arrives intact to verify wherever it is published from.
### How can we reproduce it (as minimally and precisely as possible)?
```console
$ export GNUPGHOME=$(mktemp -d)
$ gpg --batch --pinentry-mode loopback --passphrase '' \
--quick-generate-key 'Helm Repro ' rsa2048 sign 1d
$ gpg --export-secret-keys > "$GNUPGHOME/secring.gpg"
$ gpg --export > "$GNUPGHOME/pubring.gpg"
$ docker run -d --rm -p 5099:5000 registry:2.8.3
$ helm create bar
$ helm package --sign --key 'Helm Repro' --keyring "$GNUPGHOME/secring.gpg" bar
$ helm push bar-0.1.0.tgz oci://localhost:5099/charts --plain-http
$ oras cp --from-plain-http --to-plain-http \
localhost:5099/charts/bar:0.1.0 localhost:5099/charts/foo:0.1.0
$ helm pull oci://localhost:5099/charts/bar:0.1.0 --plain-http --verify --keyring "$GNUPGHOME/pubring.gpg"
Chart Hash Verified: sha256:ecd61ca508db32cb765021964651a22eec5ce33dbefe8c8eb934853f1b25edd1
$ helm pull oci://localhost:5099/charts/foo:0.1.0 --plain-http --verify --keyring "$GNUPGHOME/pubring.gpg"
Error: provenance does not contain a SHA for a file named "foo-0.1.0.tgz"
```
The copy step only moves the artifact to a second repository; the chart, its layers and its provenance are byte for byte the ones that were pushed.
### Helm version
```console
$ helm version
version.BuildInfo{Version:"v4.2.3", GitCommit:"43e8b7feece8beb0fcba47059ec9b522fd929a64", GitTreeState:"clean", GoVersion:"go1.26.5", KubeClientVersion:"v1.36"}
```
The download path is unchanged on `main`.
### Kubernetes version
Not involved, the failure is in the downloader before any cluster is contacted.
Contributor guide
Research direction
Start with pkg/downloader/chart_downloader.go at lines 156-159 and 311-320, then read the manifest lookup in pkg/provenance/sign.go:266. Reproduce the signed-chart pull using the commands in the issue and trace the archive name used for verification. Done means the copied chart verifies under a repository name different from its chart name without changing signature semantics.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100