From 199394934de5415ced4fe2d9386cd1ccdd0bada4 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 7 Nov 2025 10:17:10 -0500 Subject: [PATCH] preserve --from order (#4350) Signed-off-by: Alex Goodman --- cmd/syft/internal/commands/cataloger_list.go | 4 +- cmd/syft/internal/options/catalog.go | 13 ++- cmd/syft/internal/options/catalog_test.go | 94 +++++++++++++++++++- 3 files changed, 104 insertions(+), 7 deletions(-) diff --git a/cmd/syft/internal/commands/cataloger_list.go b/cmd/syft/internal/commands/cataloger_list.go index 1dcb4b121..18d95c307 100644 --- a/cmd/syft/internal/commands/cataloger_list.go +++ b/cmd/syft/internal/commands/cataloger_list.go @@ -87,8 +87,8 @@ func runCatalogerList(opts *catalogerListOptions) error { } func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task) (string, error) { - defaultCatalogers := options.Flatten(opts.DefaultCatalogers) - selectCatalogers := options.Flatten(opts.SelectCatalogers) + defaultCatalogers := options.FlattenAndSort(opts.DefaultCatalogers) + selectCatalogers := options.FlattenAndSort(opts.SelectCatalogers) selectedTaskGroups, selectionEvidence, err := task.SelectInGroups( allTaskGroups, cataloging.NewSelectionRequest(). diff --git a/cmd/syft/internal/options/catalog.go b/cmd/syft/internal/options/catalog.go index 6f33f7b9e..e365b7416 100644 --- a/cmd/syft/internal/options/catalog.go +++ b/cmd/syft/internal/options/catalog.go @@ -284,10 +284,10 @@ func (cfg *Catalog) PostLoad() error { cfg.From = Flatten(cfg.From) - cfg.Catalogers = Flatten(cfg.Catalogers) - cfg.DefaultCatalogers = Flatten(cfg.DefaultCatalogers) - cfg.SelectCatalogers = Flatten(cfg.SelectCatalogers) - cfg.Enrich = Flatten(cfg.Enrich) + cfg.Catalogers = FlattenAndSort(cfg.Catalogers) + cfg.DefaultCatalogers = FlattenAndSort(cfg.DefaultCatalogers) + cfg.SelectCatalogers = FlattenAndSort(cfg.SelectCatalogers) + cfg.Enrich = FlattenAndSort(cfg.Enrich) // for backwards compatibility cfg.DefaultCatalogers = append(cfg.DefaultCatalogers, cfg.Catalogers...) @@ -312,6 +312,11 @@ func Flatten(commaSeparatedEntries []string) []string { out = append(out, strings.TrimSpace(s)) } } + return out +} + +func FlattenAndSort(commaSeparatedEntries []string) []string { + out := Flatten(commaSeparatedEntries) sort.Strings(out) return out } diff --git a/cmd/syft/internal/options/catalog_test.go b/cmd/syft/internal/options/catalog_test.go index d74931be1..7753da5c6 100644 --- a/cmd/syft/internal/options/catalog_test.go +++ b/cmd/syft/internal/options/catalog_test.go @@ -79,6 +79,98 @@ func TestCatalog_PostLoad(t *testing.T) { } } +func TestFlatten(t *testing.T) { + tests := []struct { + name string + input []string + expected []string + }{ + { + name: "preserves order of comma-separated values", + input: []string{"registry,docker,oci-dir"}, + expected: []string{"registry", "docker", "oci-dir"}, + }, + { + name: "preserves order across multiple entries", + input: []string{"registry,docker", "oci-dir"}, + expected: []string{"registry", "docker", "oci-dir"}, + }, + { + name: "trims whitespace", + input: []string{" registry , docker ", " oci-dir "}, + expected: []string{"registry", "docker", "oci-dir"}, + }, + { + name: "handles single value", + input: []string{"registry"}, + expected: []string{"registry"}, + }, + { + name: "handles empty input", + input: []string{}, + expected: nil, + }, + { + name: "preserves reverse alphabetical order", + input: []string{"zebra,yankee,xray"}, + expected: []string{"zebra", "yankee", "xray"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Flatten(tt.input) + assert.Equal(t, tt.expected, got) + }) + } +} + +func TestFlattenAndSort(t *testing.T) { + tests := []struct { + name string + input []string + expected []string + }{ + { + name: "sorts comma-separated values", + input: []string{"registry,docker,oci-dir"}, + expected: []string{"docker", "oci-dir", "registry"}, + }, + { + name: "sorts across multiple entries", + input: []string{"registry,docker", "oci-dir"}, + expected: []string{"docker", "oci-dir", "registry"}, + }, + { + name: "trims whitespace and sorts", + input: []string{" registry , docker ", " oci-dir "}, + expected: []string{"docker", "oci-dir", "registry"}, + }, + { + name: "handles single value", + input: []string{"registry"}, + expected: []string{"registry"}, + }, + { + name: "handles empty input", + input: []string{}, + expected: nil, + }, + { + name: "sorts reverse alphabetical order", + input: []string{"zebra,yankee,xray"}, + expected: []string{"xray", "yankee", "zebra"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := FlattenAndSort(tt.input) + assert.Equal(t, tt.expected, got) + }) + } +} + func Test_enrichmentEnabled(t *testing.T) { tests := []struct { directives string @@ -139,7 +231,7 @@ func Test_enrichmentEnabled(t *testing.T) { for _, test := range tests { t.Run(test.directives, func(t *testing.T) { - got := enrichmentEnabled(Flatten([]string{test.directives}), test.test) + got := enrichmentEnabled(FlattenAndSort([]string{test.directives}), test.test) assert.Equal(t, test.expected, got) }) }