Skip to content

fix: handle OCI digest algorithm prefix in chart downloader#31601

Merged
banjoh merged 6 commits intohelm:mainfrom
banjoh:em/fix-digest-tag-inconsistency
Feb 20, 2026
Merged

fix: handle OCI digest algorithm prefix in chart downloader#31601
banjoh merged 6 commits intohelm:mainfrom
banjoh:em/fix-digest-tag-inconsistency

Conversation

@banjoh
Copy link
Copy Markdown
Contributor

@banjoh banjoh commented Dec 3, 2025

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 the sha256: prefix was passed to hex.DecodeString().

Closed: #31600

Special notes for your reviewer:

If applicable:

  • this PR contains user facing changes (the docs needed label should be applied if so)
  • this PR contains unit tests
  • this PR has been tested for backwards compatibility

@pull-request-size pull-request-size bot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Dec 3, 2025
@banjoh banjoh changed the title fix: strip digest algorithm prefix from digest string fix: handle OCI digest algorithm prefix in chart downloader Dec 3, 2025
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>
@banjoh banjoh force-pushed the em/fix-digest-tag-inconsistency branch from a8697d7 to c5112b9 Compare December 3, 2025 13:52
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
@pull-request-size pull-request-size bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Dec 3, 2025
@banjoh banjoh added the bug Categorizes issue or PR as related to a bug. label Dec 3, 2025
@TerryHowe
Copy link
Copy Markdown
Contributor

Thanks for pulling to PR together so fast @banjoh !

Signed-off-by: Evans Mungai <mbuevans@gmail.com>
TerryHowe
TerryHowe previously approved these changes Dec 4, 2025
Copy link
Copy Markdown
Contributor

@TerryHowe TerryHowe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@TerryHowe TerryHowe added the Has One Approval This PR has one approval. It still needs a second approval to be merged. label Dec 13, 2025
Comment on lines +520 to +522
if result != tt.expected {
t.Errorf("stripDigestAlgorithm(%q) = %q, want %q", tt.input, result, tt.expected)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if result != tt.expected {
t.Errorf("stripDigestAlgorithm(%q) = %q, want %q", tt.input, result, tt.expected)
}
assert.Equal(t, tt.expected, result)

Use testify?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed

Signed-off-by: Evans Mungai <mbuevans@gmail.com>
Copilot AI review requested due to automatic review settings February 19, 2026 18:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 in ChartDownloader.
  • Add a small helper + unit test for digest-prefix stripping.
  • Add an end-to-end regression test for helm pull with tag@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 with sha256: 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.

Comment on lines 216 to 217
func (srv *OCIServer) Run(t *testing.T, opts ...OCIServerOpt) *OCIServerRunResult {
t.Helper()
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Signed-off-by: Evans Mungai <mbuevans@gmail.com>
Copy link
Copy Markdown
Contributor

@TerryHowe TerryHowe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@banjoh banjoh merged commit ee01860 into helm:main Feb 20, 2026
5 checks passed
@banjoh banjoh deleted the em/fix-digest-tag-inconsistency branch February 20, 2026 16:58
@scottrigby scottrigby removed the Has One Approval This PR has one approval. It still needs a second approval to be merged. label Mar 10, 2026
@scottrigby scottrigby added this to the 4.1.2 milestone Mar 10, 2026
@scottrigby scottrigby added the needs-pick Indicates that a PR needs to be cherry-picked into the next release candidate. label Mar 10, 2026
@scottrigby scottrigby added picked Indicates that a PR has been cherry-picked into the next release candidate. and removed needs-pick Indicates that a PR needs to be cherry-picked into the next release candidate. labels Mar 11, 2026
scottrigby pushed a commit that referenced this pull request Mar 11, 2026
* 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)
@scottrigby scottrigby modified the milestones: 4.1.2, 4.1.3 Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Categorizes issue or PR as related to a bug. picked Indicates that a PR has been cherry-picked into the next release candidate. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent digest + tag handling in helm 4 install with OCI Chart

5 participants