The string returned by the string builder was cast to a byte slice
anyways. Remove this indirection. Also, write directly to the buffer
instead of using fmt.Fprintln(...).
Signed-off-by: Tom Wieczorek <twieczorek@mirantis.com>
* fix: route registry messages to stderr in template and show
When pulling an OCI chart, the registry client prints "Pulled: ..." and
"Digest: ..." status lines (and deprecation/underscore warnings) to its
configured output writer. Since v4.2.1 (introduced by #32056), these
messages leaked into the stdout output of helm template and helm show,
breaking downstream consumers such as cdk8s and other YAML parsers.
Fix by passing the command's stderr to the registry client in the
template and show commands instead of stdout. This keeps stdout clean
for machine-readable YAML while still surfacing registry warnings and
status messages on stderr for troubleshooting, rather than discarding
them. The pull/push commands continue to print these messages on their
normal output writer.
The show command's addRegistryClient writer parameter is renamed to
registryOut and wired through to the registry client, so it is no longer
a no-op.
Fixes#32215
Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com>
* test(cmd): cover registry client stderr routing via OCI pull
Exercise helm template and helm show against an in-process OCI registry
(repotest.NewOCIServer) and assert Pulled:/Digest: status lines appear
on stderr only, keeping stdout free of registry noise.
Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com>
* test(cmd): drop weak addRegistryClient unit test
OCI pull coverage already asserts Pulled/Digest land on stderr, not stdout.
Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com>
---------
Signed-off-by: amarkdotdev <amarkdotdev@users.noreply.github.com>
Co-authored-by: amarkdotdev <amarkdotdev@users.noreply.github.com>
* refactor: remove per-file decompression size limit
Remove MaxDecompressedFileSize as it's no longer necessary after
migrating to a maintained JSON schema library (santhosh-tekuri/jsonschema/v6).
The original limit was added to protect against vulnerabilities in an
unmaintained library.
The total decompressed chart size limit (MaxDecompressedChartSize) remains
to protect against other attack vectors.
Partially resolves#30738
Related:
- https://github.com/helm/helm/pull/30743
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix: deprecate MaxDecompressedFileSize instead of removing
As Matt suggested we should keep the variable until v5 as it can be used
because it is public.
Related:
- https://github.com/helm/helm/pull/31748#discussion_r2738518696
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix: enforce aggregate size budget on directory loading
Directory-based chart loading (`LoadDir`) used unbounded `os.ReadFile`
calls with no total size check. Archive loading already enforces
`MaxDecompressedChartSize` via a remaining-byte budget but directory
loading did not, leaving local charts and `file://` dependencies as
an unbounded memory path.
Add `ReadFileWithBudget` in the archive package and use it in both
v2 and v3 directory loaders so they track the same aggregate budget.
Ref: https://github.com/helm/helm/pull/31748#issuecomment-4138927643
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix: cap directory budget reads with LimitReader
Use `os.Open` + `io.LimitReader` instead of `os.ReadFile` in
`ReadFileWithBudget` so a file that grows between stat and read
cannot allocate unbounded memory.
Also fix `MaxDecompressedFileSize` doc comment to reflect it is
unused/deprecated, add nil guard on remaining, and check
`os.Stat` errors in tests.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* test: add v3 directory loader budget test
Mirror the v2 `TestLoadDirExceedsBudget` test for the v3 loader
to prevent budget enforcement regressions in either path.
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* refactor(loader): make read budget configurable
Follow recommendations from https://github.com/helm/helm/pull/31748#discussion_r3058581419
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(loader): export BudgetedReader for cross-package use
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
* fix(loader): rename max param to avoid shadowing built-in
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
---------
Signed-off-by: Benoit Tigeot <benoit.tigeot@lifen.fr>
This is needed for goreleaser to create updates. Its latest release
needs it.
Updates needed for linting and to fix failed tests resulting from
Go std library changes.
Signed-off-by: Matt Farina <matt.farina@suse.com>
* fix(provenance): support GnuPG keybox (pubring.kbx) keyrings
Starting with GnuPG 2.1, file-backed public keyrings can use
~/.gnupg/pubring.kbx instead of the legacy pubring.gpg. Helm only read
the legacy format, so chart and plugin verification failed on
installations using the file-backed keybox.
Make the keyring loader format-aware:
- GnuPG keybox (pubring.kbx): extract OpenPGP keyblocks from the
keybox container without adding a dependency. Skip ephemeral blobs,
matching GnuPG's own read behavior.
- ASCII-armored keyrings: load single or concatenated exports.
- Legacy binary packet streams (pubring.gpg): retain the existing path.
defaultKeyring() falls back to pubring.kbx when pubring.gpg is absent.
pubring.gpg keeps precedence when both files exist.
This change covers file-backed public keyrings. It does not read the
SQLite database used by keyboxd, which needs a separate design.
Related to #31836
Signed-off-by: Ruslan Shaydullin <shaydullin.r.d@outlook.com>
* fix(provenance): treat only not-exist as keyring absence in defaultKeyring
A stat error other than 'not exist' (e.g. a permission problem) meant
the file may well be present, but defaultKeyring() skipped past it: an
unreadable pubring.gpg silently lost precedence to pubring.kbx, and the
surfaced error could point at the wrong file. Treat only fs.ErrNotExist
as absence. For any other stat error, return that path unchanged so the
real error surfaces when the keyring is opened.
Signed-off-by: Ruslan Shaydullin <shaydullin.r.d@outlook.com>
---------
Signed-off-by: Ruslan Shaydullin <shaydullin.r.d@outlook.com>
Co-authored-by: George Jenkins <gvjenkins@gmail.com>
A descriptor's declaration decides nothing on its own. An entry may list
manifests rather than hold an artifact, which a multi-arch image beside a
chart does, and it may declare an artifact type that is not the chart's,
which a builder setting the field wrongly leaves behind. Ruling on either
from the descriptor loses charts that are there: the list is never searched,
or the entry is dropped before its config is read, and in both cases the
chart beside it becomes the only one in the index and answers in its place.
Search what an entry lists, the same way the top index is searched, whether
it carries the OCI index media type or the Docker manifest list one, and
read the config of every entry whose declaration did not match, not only of
those that declared nothing. A list is still never a candidate: the artifact
is identified by the config blob its manifest carries and a list has none,
so taking one would root the copy at a manifest the filter drops. Both
readings happen under the condition that already governs the second pass, so
an index whose declared entries answer the reference is resolved without
either.
Reading an entry means reading what a registry serves for it, so the reading
is bounded twice over. An entry declaring more bytes than a manifest is goes
unread, and so does one whose body holds neither the config nor the list of
manifests its media type promised, since a body that parses as both shapes
proves nothing about either. The search itself stops three lists down,
counting from the one the reference resolves to, against a chain a registry
is free to extend while it is walked.
None of those three is dropped quietly. Each is recorded the way an entry
that cannot be fetched is, because every answer resting on nothing else in
the index matching has to know the difference between an index that holds
one chart and a pass that read one.
A manifest the copy will not store cannot be the answer either. A chart
written to the Docker schema, which is what a registry-to-registry copy
leaves behind, is copied like the OCI one it describes, and a selection
that lands on anything else the pull does not accept says so and names
the entry, rather than leaving a copy that quietly stores nothing and a
message about descriptors that never arrived.
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Assisted-By: Claude <noreply@anthropic.com>
Pulling a reference that resolves to an Image Index collected every
descriptor matching a fixed media-type allow-list, then assigned the
config, chart and provenance descriptors with a switch that kept the
last match of each kind. That slice is filled from oras PreCopy
callbacks during a concurrent graph walk, so its order is neither the
order of the index nor defined by anything else, and each kind is
resolved independently of the others. For an index holding more than
one chart the delivered chart is arbitrary, and because the metadata
is read from the config blob while the bytes come from the chart
layer, the two can describe different charts. Nothing on the path
compares them, and the file name is taken from the reference rather
than from either.
Select the chart manifest before copying instead. An entry declaring
the chart artifactType is a candidate, whether or not it also carries
a platform, so that tooling stamping a platform on every index entry
cannot hide the chart. Entries declaring no type are matched on their
config mediaType, which keeps an index written before artifactType
existed resolvable. That second pass runs when the first found
nothing, and also when no single entry it found announces the whole
requested name and version, since narrowing the candidates before
that is checked is how a mixed index answers with the wrong chart.
Asking per attribute instead would let one entry supply the name and
another the version, and the pair they form belongs to neither.
Several candidates are then narrowed by the requested name and, if
that leaves more than one, by the requested version. Both are read
from the descriptor annotations, falling back to the candidate's own
config for whichever an index omits. Entries repeated in the index
are collapsed first, so one chart is never called ambiguous with
itself, and an answer that rests on nothing else matching is withheld
when the pass could not read every entry, because absence is a fact
about the index only while the whole index could be read. Reading a
candidate's identity can fail the same way and now says so, so a
chart whose name could not be fetched is no longer indistinguishable
from a chart that is not there. A choice that name and version cannot
settle is an error naming the candidates rather than an arbitrary
pick.
One consequence worth naming. Pulling an index reference reports the
digest of the selected chart manifest where it previously reported
the digest of the index. Selection runs inside the copy, on the root
oras has already resolved, so narrowing an index costs no request of
its own. The pass over the entries that declare no type is what costs
requests, one per entry, and only when it runs.
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Assisted-By: Claude <noreply@anthropic.com>
YAMLReader can return EOF without yielding the last line when that line
has no trailing newline and its length is a multiple of bufio's default
buffer. Read the file fully and ensure a trailing newline before parsing
so compact JSON values files are not silently ignored.
Fixes#32506
Signed-off-by: Dean Chen <862469039@qq.com>
* fix: bump go.opentelemetry.io/otel to v1.44.0 for GO-2026-5158
govulncheck on main flags GO-2026-5158 in go.opentelemetry.io/otel@v1.43.0
(fixed in v1.44.0). Bump the transitive otel dependency to clear the
vulnerability.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
* fix: bump otel/sdk and otel/sdk/metric to v1.44.0 for version parity
Keep the lockstep-versioned OpenTelemetry core and SDK modules aligned at
v1.44.0 to avoid version skew.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
---------
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>