From 5c4faa809afdf9c36c671cb3b4ebf8a3df78d824 Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 1 Jul 2026 12:36:47 +0200 Subject: [PATCH] fix(policies): split comma-separated values on each merged input line When a policy input declared as a comma-separated value in a workflow contract was merged with a runtime value supplied via --policy-input-from-file, getInputArguments split the merged string on newlines first and returned the multi-line slice as-is, skipping the per-value comma splitting. This collapsed the contract's comma-joined segment into a single literal glob containing commas that matched nothing. Normalize consistently by splitting each line on commas as well, so a comma-separated segment is always expanded into separate values regardless of whether a runtime value was appended. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez Chainloop-Trace-Sessions: 20709f88-f555-488c-a8ae-e2d60903aff5, 7c9eb197-bbea-464f-9444-cd17ac09039c --- pkg/policies/policies.go | 23 ++++++++++------------- pkg/policies/policies_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/pkg/policies/policies.go b/pkg/policies/policies.go index bb58ac983..909f2061b 100644 --- a/pkg/policies/policies.go +++ b/pkg/policies/policies.go @@ -773,22 +773,19 @@ func getInputArguments(inputs map[string]string) map[string]any { args := make(map[string]any) for k, v := range inputs { - // scan for multiple values + // Normalize consistently: split on newlines first (multi-value inputs, + // e.g. a comma-separated contract value merged with runtime values by + // mergeRuntimeInputs), then split each line on commas. Doing both means a + // comma-separated segment is always expanded into separate values, + // regardless of whether it arrived as a single line or as one of several + // merged lines. lines := strings.Split(strings.TrimRight(v, "\n"), "\n") - value := getValue(lines) - - if value == nil { - continue - } - s, ok := value.(string) - if !ok { - // case for multivalued argument - args[k] = value + values := make([]string, 0, len(lines)) + for _, line := range lines { + values = append(values, splitArgs(line)...) } - // Single string, let's check for CSV and escaped commas `\,` - lines = splitArgs(s) - value = getValue(lines) + value := getValue(values) if value == nil { continue } diff --git a/pkg/policies/policies_test.go b/pkg/policies/policies_test.go index 723993c2d..85fbe3042 100644 --- a/pkg/policies/policies_test.go +++ b/pkg/policies/policies_test.go @@ -799,6 +799,33 @@ func (s *testSuite) TestGetInputArguments() { inputs: map[string]string{"foo": "\n\n\nbar1\nbar2\n\nbar3\n"}, expected: map[string]any{"foo": []string{"bar1", "bar2", "bar3"}}, }, + { + // A comma-separated contract value merged with runtime values (which + // mergeRuntimeInputs joins with newlines) must still split the + // comma-joined line into separate values instead of collapsing it into + // a single literal glob containing commas. + name: "multiline input where a line is comma-separated", + inputs: map[string]string{"foo": "**/vendor/**,**/redist/**\nC:/a\nC:/b"}, + expected: map[string]any{"foo": []string{"**/vendor/**", "**/redist/**", "C:/a", "C:/b"}}, + }, + { + name: "multiline input where several lines are comma-separated", + inputs: map[string]string{"foo": "a,b\nc,d\ne"}, + expected: map[string]any{"foo": []string{"a", "b", "c", "d", "e"}}, + }, + { + name: "multiline input with escaped comma on a line", + inputs: map[string]string{"foo": "a\\,b,c\nd"}, + expected: map[string]any{"foo": []string{"a,b", "c", "d"}}, + }, + { + // A multiline input that reduces to a single non-empty value after + // blank-line filtering must return a plain string, not a one-element + // slice, so the two-stage split never over-wraps single values. + name: "multiline input collapsing to a single value", + inputs: map[string]string{"foo": "\nbar\n"}, + expected: map[string]any{"foo": "bar"}, + }, { name: "no input", inputs: nil,