Uh oh!
There was an error while loading. Please reload this page.
fix(flags): reject an empty element in a multi-value flag - #1092
Merged
Conversation
Contributor
Claude finished @JonJagger's task in 2m 2s —— View job Review — all prior feedback addressed ✅Re-reviewed after the latest push ( Static checks
Prior review points, now resolved
What's good here
Minor, optional (non-blocking)
Nothing blocking. LGTM.
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
pflag's StringSlice splits each value with a CSV reader, and that reader yields nothing for an empty string. An empty element is therefore appended as nothing and leaves no trace: after parsing, `--attachments "" --attachments file` cannot be told apart from `--attachments file`, so nothing downstream can report what was lost. Two flags where that matters now use a value type that refuses an empty element at Set, the last point where it still exists. --attachments is the case this bug was filed for: an attestation is recorded without evidence its author believed was on it. --template is worse in kind, because it names the attestations a flow requires, so a dropped element weakens that flow for every artifact passing through it afterwards rather than spoiling one record. Comma splitting is kept rather than switching to pflag's StringArray, which stores values verbatim. An environment variable cannot be repeated, so a comma list is the only way to give a multi-value flag more than one value from the environment; a regression test pins that. The type implements pflag.SliceValue as well as pflag.Value, so the refusal also covers the config file, which bindFlags applies through Replace.
JonJaggerforce-pushed
the
fix-empty-element-in-multi-value-flag
branch
from
August 12, 2026 14:41
8c90ed1 to
cffebaeCompareUh oh!
There was an error while loading. Please reload this page.
…fusal docs(flags): say how config and env values reach the empty-element refusal The comments claimed bindFlags applies a config file list through Replace. It does not: it stringifies the value with %v and applies it through Set, so Append, Replace and GetSlice have no caller in this CLI at all. Left as written, the next maintainer would look for a config path that does not exist, and might take the SliceValue methods for load-bearing code. They now say what is true: the refusal reaches config and environment values through Set, by the same path as the command line, and pflag.SliceValue is implemented as future-proofing for the first caller that asserts to it. A test now pins the config/env guarantee rather than leaving it to be inferred from the two mechanisms lining up. Without the new flag type it fails on "lstat : no such file or directory" - the empty element surviving into a confusing error rather than a clear refusal, which is the symptom this work exists to remove. Please enter the commit message for your changes. Lines starting
--template and --attachments share the value type, so refusing an empty element from KOSLI_TEMPLATE already held transitively from the attachments env test. Nothing said so where a reader of createFlow would look, leaving them to find the guarantee in another command's test file. An environment variable cannot be repeated, so a comma list is the only way to name several required attestations that way, which makes it the shape that silently drops one.
JonJagger
enabled auto-merge (squash)
August 12, 2026 15:28
AlexKantor87
approved these changes
Aug 12, 2026
Uh oh!
There was an error while loading. Please reload this page.
JonJagger added a commit
that referenced
this pull request
Aug 15, 2026
…ield The audit drives the CLI, so it can only answer half the question. When an empty value produces the same request as omitting the flag - which is 172 of the 189 the CLI does not refuse - the server never receives anything empty to reject, and no amount of running the CLI harder will make it. replay.py asks the other half by capturing a request and sending it again with one field emptied. It reads the same spec.json, reuses the audit's own invocations so it captures what the audit measures, and writes results-api.tsv. A separate script rather than a third mode of audit.py: --ci asks the same question in another environment and its rows line up with the plain run's, while these rows are about a captured request and do not. The mapping from a flag to the payload field it controls is measured, not written down: the payload of a run with the flag set, diffed against one with it omitted. The field is emptied here rather than by the CLI, which is why this keeps working after the CLI starts refusing empty values. requests.go now logs the method beside the URL. Without it the probe would have needed a hand-written table of which endpoint takes which verb, which is the thing this approach exists to avoid. Of 412 rows, 19 are an answer about the server: 11 fields it accepts empty and 8 it refuses. The eight refusals are mostly types rather than emptiness - a boolean, an enum, two objects, a URL. Among the eleven, `create flow --template` is refused by the CLI by the type added in #1092 and accepted by the API, which stores a flow requiring an attestation whose name is empty. That is the decision document's "what a CLI rule cannot reach" argument with a measured instance behind it. Two things the control replay caught, and both are the point of having one. A captured request that was never valid gives a 400 beside an emptied 400, which reads as a refusal and is not: 35 rows say so rather than pretending. And chasing why those controls failed led to the CLI audit's own controls, where 114 of 412 runs with a supposedly real value fail. The audit invents the values it gives flags. Written up, with the tempting remedy measured and rejected: the server publishes a schema, and it would fix 6 of the 114. Most rows are not an answer, and mostly for good reasons the file states rather than hides. The read commands put their flags in the query string and send no body, which is 85 rows and the largest thing still missing. Some flags never leave the machine. `kosli fingerprint` sends nothing at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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 freeto 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.
pflag's StringSlice splits each value with a CSV reader, and that reader yields
nothing for an empty string. An empty element is therefore appended as nothing
and leaves no trace: after parsing,
--attachments "" --attachments filecannotbe told apart from
--attachments file, so nothing downstream can report whatwas lost.
Two flags where that matters now use a value type that refuses an empty element
at Set, the last point where it still exists. --attachments is the case this bug
was filed for: an attestation is recorded without evidence its author believed
was on it. --template is worse in kind, because it names the attestations a flow
requires, so a dropped element weakens that flow for every artifact passing
through it afterwards rather than spoiling one record.
Comma splitting is kept rather than switching to pflag's StringArray, which
stores values verbatim. An environment variable cannot be repeated, so a comma
list is the only way to give a multi-value flag more than one value from the
environment; a regression test pins that.
The type implements pflag.SliceValue as well as pflag.Value, so the refusal also
covers the config file, which bindFlags applies through Replace.
Checklist
charts/k8s-reporter/) updated, if needed. Note: these changes live in a separate PR