fix: handle OCI digest algorithm prefix in chart downloader#31601
fix: handle OCI digest algorithm prefix in chart downloader#31601
Conversation
OCI references with tag+digest (e.g., chart:1.0@sha256:abc...) failed with "invalid byte" error because the sha256: prefix was passed to hex.DecodeString(). Signed-off-by: Evans Mungai <mbuevans@gmail.com>
a8697d7 to
c5112b9
Compare
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
|
Thanks for pulling to PR together so fast @banjoh ! |
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
| if result != tt.expected { | ||
| t.Errorf("stripDigestAlgorithm(%q) = %q, want %q", tt.input, result, tt.expected) | ||
| } |
There was a problem hiding this comment.
| if result != tt.expected { | |
| t.Errorf("stripDigestAlgorithm(%q) = %q, want %q", tt.input, result, tt.expected) | |
| } | |
| assert.Equal(t, tt.expected, result) |
Use testify?
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
There was a problem hiding this comment.
Pull request overview
Fixes Helm’s OCI chart download path for references that include both a tag and a digest (e.g., chart:1.0@sha256:...) by ensuring the digest algorithm prefix isn’t passed into hex.DecodeString().
Changes:
- Normalize digest strings by stripping the
<algo>:prefix before hex decoding inChartDownloader. - Add a small helper + unit test for digest-prefix stripping.
- Add an end-to-end regression test for
helm pullwithtag@digest, and adjust OCI test server helper to return the pushed digest.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| pkg/repo/v1/repotest/server.go | Changes OCI test server helper to return pushed-chart metadata for tests. |
| pkg/downloader/chart_downloader.go | Strips digest algorithm prefix before hex decoding in download/cache paths. |
| pkg/downloader/chart_downloader_test.go | Adds unit test coverage for digest-prefix stripping helper. |
| pkg/cmd/pull_test.go | Adds regression test for pulling OCI charts with tag+digest. |
Comments suppressed due to low confidence (3)
pkg/downloader/chart_downloader.go:134
- After decoding the digest, the code copies it into a fixed [32]byte buffer without validating the decoded length. If a non-SHA256 digest ever comes through (or any digest not exactly 32 bytes), this will silently truncate/pad and can lead to incorrect cache keys and potential collisions. Consider validating that the decoded digest is exactly sha256.Size bytes (and/or that any algorithm prefix is specifically sha256) before copying into
digest32.
var digest []byte
var digest32 [32]byte
if hash != "" {
// if there is a hash, populate the other formats
// Strip the algorithm prefix (e.g., "sha256:") if present
digest, err = hex.DecodeString(stripDigestAlgorithm(hash))
if err != nil {
return "", nil, err
}
copy(digest32[:], digest)
if pth, err := c.Cache.Get(digest32, CacheChart); err == nil {
pkg/downloader/chart_downloader.go:242
- Same issue as in DownloadTo: the decoded digest is copied into a fixed [32]byte buffer without checking the decoded length. This can silently truncate/pad non-32-byte digests and produce wrong cache keys; validate
len(digest) == sha256.Size(or verify the algorithm prefix is sha256) before copying.
// Strip the algorithm prefix (e.g., "sha256:") if present
digest, err := hex.DecodeString(stripDigestAlgorithm(digestString))
if err != nil {
return "", nil, fmt.Errorf("unable to decode digest: %w", err)
}
var digest32 [32]byte
copy(digest32[:], digest)
pkg/cmd/pull_test.go:551
- This strips the digest algorithm prefix using a fixed substring offset (
[7:]), which assumes the digest always starts withsha256:and is long enough. Prefer parsing on:(e.g., via strings.Cut/SplitN) so the test is robust to digest algorithm/format changes and avoids a potential out-of-range panic if the digest string is unexpected.
digestPart := result.PushedChart.Manifest.Digest[7:] // strip "sha256:"
expectedFile = filepath.Join(outdir, fmt.Sprintf("oci-dependent-chart@sha256-%s.tgz", digestPart))
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
pkg/repo/v1/repotest/server.go
Outdated
| func (srv *OCIServer) Run(t *testing.T, opts ...OCIServerOpt) *OCIServerRunResult { | ||
| t.Helper() |
There was a problem hiding this comment.
Changing OCIServer.Run to return a value is a breaking API change for this test helper: any existing call sites that previously did ociSrv.Run(t) will now fail to compile unless they use/ignore the returned value. Either update all call sites in this repo to handle the return value, or keep Run as void and introduce a separate method (e.g., RunWithResult) for callers that need the push digest/result.
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
* fix: strip digest algorithm prefix before hex decoding OCI references with tag+digest (e.g., chart:1.0@sha256:abc...) failed with "invalid byte" error because the sha256: prefix was passed to hex.DecodeString(). Signed-off-by: Evans Mungai <mbuevans@gmail.com> * Add cmd test for OCI references with tag+digest Signed-off-by: Evans Mungai <mbuevans@gmail.com> * Move oci registry push result to a struct Signed-off-by: Evans Mungai <mbuevans@gmail.com> * Review comments from PR review Signed-off-by: Evans Mungai <mbuevans@gmail.com> * Fix failing test Signed-off-by: Evans Mungai <mbuevans@gmail.com> --------- Signed-off-by: Evans Mungai <mbuevans@gmail.com> (cherry picked from commit ee01860)
What this PR does / why we need it:
OCI references with tag+digest (e.g.,
chart:1.0@sha256:abc...) failed with "invalid byte" error because thesha256:prefix was passed tohex.DecodeString().Closed: #31600
Special notes for your reviewer:
If applicable:
docs neededlabel should be applied if so)