Error instead of silently truncating split set values - #1869
Draft
northbymidwest wants to merge 1 commit into
Draft
Error instead of silently truncating split set values#1869northbymidwest wants to merge 1 commit into
northbymidwest wants to merge 1 commit into
Conversation
helm's value parser treats "," as a separator between assignments, so a set,
set_sensitive or set_list value containing an unescaped comma is split. The key
keeps only the text before the comma and the remainder is applied as unrelated
keys.
When that remainder contains no "=", strvals reports "key ... has no value" and
the apply fails, which is how this is usually noticed. When it does contain one
-- a PEM bundle, a base64 blob, a comment header -- it parses cleanly instead,
so the release is applied with a truncated value and helm exits 0. Nothing in
the plan or the apply output indicates anything was dropped.
A single set entry is expected to produce exactly one leaf, and a set_list entry
as many elements as it was given. Both are now checked before the value is
parsed into the release config, and a mismatch is reported with the escape that
fixes it. Brace list syntax ("{a,b}") still parses to one leaf, so it keeps
working.
The error path leaked as well, and is fixed with it. The parser errors quote the
fragment they choked on, which for a split value is the part after the comma, so
interpolating one into a diagnostic printed part of a set_sensitive or set_wo
value in plan output:
Failed parsing key "secrets.token": key " MORE-SECRET-MATERIAL" has no value
getValue now takes whether the entry is sensitive and withholds the underlying
error for those, giving the escaping hint instead; set_wo counts, since
write-only values are secrets by construction. Non-sensitive entries keep the
parser error, which is the more useful diagnostic where nothing is at stake. The
set_sensitive loop also logged its whole model at debug level, and %v on it
renders the value, so it now logs the key name alone.
Verified against chart 1.3.13 driven with helm: a 248KB, 146-certificate bundle
containing commas and backslashes renders byte-identical when escaped, and the
same bundle unescaped reproduces the truncation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
northbymidwest
force-pushed
the
fix-silent-set-truncation
branch
from
August 14, 2026 04:22
ddc565e to
fafad45
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rollback Plan
If a change needs to be reverted, we will publish an updated version of the library.
Changes to Security Controls
Yes, two, both narrowing what reaches user-visible output:
Failed parsing key %q with value %s: %s), so aset_sensitiveorset_wovalue was printed in plan output whenever parsing it failed. The underlying parser error is withheld for those entries too, since it quotes the fragment it choked on.set_sensitiveloop logged its whole element at debug level;%von that model renders the value. It now logs the key name alone.Non-sensitive entries keep the underlying parser error, which is the more useful diagnostic where nothing is at stake.
Description
set,set_sensitiveandset_listvalues are parsed bystrvals, which treats,as a separator between assignments. A value containing an unescaped comma is therefore split: the key keeps only the text before it, and the remainder is applied as further, unrelated keys.There are two outcomes, and only one is visible:
=,strvalsreturnskey "…" has no valueand the apply fails. This is how the behaviour is usually noticed, and is the subject of most existing reports.=— a PEM bundle, a base64 blob, a comment header — it parses cleanly. No error is returned. The release is applied with a truncated value plus some junk keys, andhelmexits 0. Nothing in the plan or the apply output indicates that anything was dropped.The second case is what this fixes. A CA bundle passed through
set_sensitivereached the chart as a single certificate, because one of its comment headers contained a comma and the base64 payload after it happened to contain=.What changed
A single
setentry is expected to produce exactly one leaf, and aset_listentry as many elements as it was given. Both are now checked against a probe parse before the value is merged into the release config, and a mismatch is reported with the escape that fixes it:Brace list syntax (
{a,b}) still parses to a single leaf, so passing a list throughsetkeeps working, as does an already-escapedx\,y. An emptyset_listis skipped, since{}parses as one empty element and that is pre-existing behaviour rather than a split.The diagnostics deliberately name only the key, never the value or elements, for the reason given under Changes to Security Controls.
Compatibility
This is breaking for a configuration that relies on the split —
value = "1,b=2"undername = "a"to set two keys from one entry. Such a configuration does work today, so this is a real break rather than a no-op, but it is not a documented behaviour: the docs describenameas the full name of the variable andvalueas its value, and instruct that,be escaped. Nothing in the repository's tests or examples relies on it. The multi-assignment form is also never written by the user —nameandvalueare supplied separately and the provider joins them — so a second assignment can only arise from a comma inside a value, which is the case this fixes.Checked against the patterns that are documented, all still accepted: escaped dots in
name, brace list syntax ({a,b}), and an already-escapedx\,y.Known gap
A brace-wrapped value containing commas is still parsed as a list rather than a string —
value = "{\"timeout\": \"30s\", \"x\": \"y\"}"becomes a two-element list. That is unchanged behaviour, not a regression, and this guard does not catch it because a list is a single leaf. Rejecting it would require either refusing brace values, which breaks the documented list syntax, or requiringtype = "string"to yield a string, which would reject brace lists that configurations may rely on. Left alone deliberately.Acceptance tests
Unit tests rather than acceptance tests, since the behaviour is entirely in value parsing and needs no cluster:
type = "string"and forauto=all still acceptedset_listgaining elements, and the empty-list edge caseset_sensitiveparse failure keeping the value out of the diagnostic, while a non-sensitive one keeps the parser errorVerified end to end outside the test suite as well, against a real chart driven with
helm: a 248KB, 146-certificate bundle containing 49 commas and 24 backslashes renders byte-identical when escaped, and the same bundle unescaped reproduces the truncation. Stubbing the guard out makes the new tests fail with the original bug rather than passing vacuously.Release Note
References
Adjacent, none overlapping:
set_sensitivefailing to redact when the key contains an escaped dot. Same underlying theme ofstrvalsescaping being handled inconsistently, but at the other end of the pipeline: that is redaction of a parsed key path, this is correctness of the value being parsed. Fix/set sensitive escaped dots (fixes #737 #1788) #1866 addssplitKeyPath(), whichlookupPath()here should use instead of its ownstrings.Splitonce that merges — noted in a comment on the function.sensitiveattribute anymore #1221 / the provider continues to reveal sensitive variables during destroy or update in-place #1287 / Fix #1287, #1221: Mark values and metadata as sensitive to prevent sensitive data leakage in plan output #1817 —metadata.valuesbeing non-sensitive, so values-supplied secrets appear in plan output. Also unaddressed here.Community Note