Add file catalogers to selection configuration (#3505)

* add file catalogers to selection configuration

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix typos

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* warn when there is conflicting file cataloging configuration

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* allow for explicit removal of all package and file tasks

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* address PR feedback

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2025-02-03 19:10:17 +00:00
committed by GitHub
parent 5e2ba43328
commit 684b6e3f98
25 changed files with 1206 additions and 353 deletions
+27
View File
@@ -0,0 +1,27 @@
package task
import (
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/filecataloging"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
)
type CatalogingFactoryConfig struct {
ComplianceConfig cataloging.ComplianceConfig
SearchConfig cataloging.SearchConfig
RelationshipsConfig cataloging.RelationshipsConfig
DataGenerationConfig cataloging.DataGenerationConfig
PackagesConfig pkgcataloging.Config
FilesConfig filecataloging.Config
}
func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig {
return CatalogingFactoryConfig{
ComplianceConfig: cataloging.DefaultComplianceConfig(),
SearchConfig: cataloging.DefaultSearchConfig(),
RelationshipsConfig: cataloging.DefaultRelationshipsConfig(),
DataGenerationConfig: cataloging.DefaultDataGenerationConfig(),
PackagesConfig: pkgcataloging.DefaultConfig(),
FilesConfig: filecataloging.DefaultConfig(),
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
)
var expressionNodePattern = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9-+]*)+$`)
@@ -142,7 +142,7 @@ func (ec expressionContext) newExpression(exp string, operation Operation, token
}
}
func newExpressionsFromSelectionRequest(nc *expressionContext, selectionRequest pkgcataloging.SelectionRequest) Expressions {
func newExpressionsFromSelectionRequest(nc *expressionContext, selectionRequest cataloging.SelectionRequest) Expressions {
var all Expressions
for _, exp := range selectionRequest.DefaultNamesOrTags {
+2 -2
View File
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
)
func Test_newExpressionsFromSelectionRequest(t *testing.T) {
@@ -135,7 +135,7 @@ func Test_newExpressionsFromSelectionRequest(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
req := cataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
result := newExpressionsFromSelectionRequest(nc, req)
if tt.expectedErrors != nil {
+40
View File
@@ -0,0 +1,40 @@
package task
import (
"fmt"
"sort"
"strings"
"github.com/scylladb/go-set/strset"
)
type factory func(cfg CatalogingFactoryConfig) Task
type Factories []factory
func (f Factories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error) {
var allTasks []Task
taskNames := strset.New()
duplicateTaskNames := strset.New()
var err error
for _, fact := range f {
tsk := fact(cfg)
if tsk == nil {
continue
}
tskName := tsk.Name()
if taskNames.Has(tskName) {
duplicateTaskNames.Add(tskName)
}
allTasks = append(allTasks, tsk)
taskNames.Add(tskName)
}
if duplicateTaskNames.Size() > 0 {
names := duplicateTaskNames.List()
sort.Strings(names)
err = fmt.Errorf("duplicate cataloger task names: %v", strings.Join(names, ", "))
}
return allTasks, err
}
+58 -27
View File
@@ -6,6 +6,7 @@ import (
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cataloging/filecataloging"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/file/cataloger/executable"
"github.com/anchore/syft/syft/file/cataloger/filecontent"
@@ -15,14 +16,27 @@ import (
"github.com/anchore/syft/syft/sbom"
)
func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash) Task {
if selection == file.NoFilesSelection || len(hashers) == 0 {
return nil
func DefaultFileTaskFactories() Factories {
return Factories{
newFileDigestCatalogerTaskFactory("digest"),
newFileMetadataCatalogerTaskFactory("file-metadata"),
newFileContentCatalogerTaskFactory("content"),
newExecutableCatalogerTaskFactory("binary-metadata"),
}
}
digestsCataloger := filedigest.NewCataloger(hashers)
func newFileDigestCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newFileDigestCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Hashers, tags...)
}
}
func newFileDigestCatalogerTask(selection file.Selection, hashers []crypto.Hash, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if selection == file.NoFilesSelection || len(hashers) == 0 {
return nil
}
accessor := builder.(sbomsync.Accessor)
coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
@@ -30,7 +44,7 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
return nil
}
result, err := digestsCataloger.Catalog(ctx, resolver, coordinates...)
result, err := filedigest.NewCataloger(hashers).Catalog(ctx, resolver, coordinates...)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileDigests = result
@@ -39,17 +53,21 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
return err
}
return NewTask("file-digest-cataloger", fn)
return NewTask("file-digest-cataloger", fn, commonFileTags(tags)...)
}
func NewFileMetadataCatalogerTask(selection file.Selection) Task {
if selection == file.NoFilesSelection {
return nil
func newFileMetadataCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newFileMetadataCatalogerTask(cfg.FilesConfig.Selection, tags...)
}
}
metadataCataloger := filemetadata.NewCataloger()
func newFileMetadataCatalogerTask(selection file.Selection, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if selection == file.NoFilesSelection {
return nil
}
accessor := builder.(sbomsync.Accessor)
coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
@@ -57,7 +75,7 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
return nil
}
result, err := metadataCataloger.Catalog(ctx, resolver, coordinates...)
result, err := filemetadata.NewCataloger().Catalog(ctx, resolver, coordinates...)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileMetadata = result
@@ -66,20 +84,24 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
return err
}
return NewTask("file-metadata-cataloger", fn)
return NewTask("file-metadata-cataloger", fn, commonFileTags(tags)...)
}
func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
if len(cfg.Globs) == 0 {
return nil
func newFileContentCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newFileContentCatalogerTask(cfg.FilesConfig.Content, tags...)
}
}
cat := filecontent.NewCataloger(cfg)
func newFileContentCatalogerTask(cfg filecontent.Config, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if len(cfg.Globs) == 0 {
return nil
}
accessor := builder.(sbomsync.Accessor)
result, err := cat.Catalog(ctx, resolver)
result, err := filecontent.NewCataloger(cfg).Catalog(ctx, resolver)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileContents = result
@@ -88,20 +110,24 @@ func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
return err
}
return NewTask("file-content-cataloger", fn)
return NewTask("file-content-cataloger", fn, commonFileTags(tags)...)
}
func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config) Task {
if selection == file.NoFilesSelection {
return nil
func newExecutableCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newExecutableCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Executable, tags...)
}
}
cat := executable.NewCataloger(cfg)
func newExecutableCatalogerTask(selection file.Selection, cfg executable.Config, tags ...string) Task {
fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if selection == file.NoFilesSelection {
return nil
}
accessor := builder.(sbomsync.Accessor)
result, err := cat.Catalog(resolver)
result, err := executable.NewCataloger(cfg).Catalog(resolver)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.Executables = result
@@ -110,7 +136,7 @@ func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config)
return err
}
return NewTask("file-executable-cataloger", fn)
return NewTask("file-executable-cataloger", fn, commonFileTags(tags)...)
}
// TODO: this should be replaced with a fix that allows passing a coordinate or location iterator to the cataloger
@@ -154,3 +180,8 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
return nil, false
}
func commonFileTags(tags []string) []string {
tags = append(tags, filecataloging.FileTag)
return tags
}
+2 -54
View File
@@ -3,12 +3,9 @@ package task
import (
"context"
"fmt"
"sort"
"strings"
"unicode"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/relationship"
@@ -23,67 +20,18 @@ import (
cpeutils "github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
)
type packageTaskFactory func(cfg CatalogingFactoryConfig) Task
type PackageTaskFactories []packageTaskFactory
type CatalogingFactoryConfig struct {
ComplianceConfig cataloging.ComplianceConfig
SearchConfig cataloging.SearchConfig
RelationshipsConfig cataloging.RelationshipsConfig
DataGenerationConfig cataloging.DataGenerationConfig
PackagesConfig pkgcataloging.Config
}
func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig {
return CatalogingFactoryConfig{
ComplianceConfig: cataloging.DefaultComplianceConfig(),
SearchConfig: cataloging.DefaultSearchConfig(),
RelationshipsConfig: cataloging.DefaultRelationshipsConfig(),
DataGenerationConfig: cataloging.DefaultDataGenerationConfig(),
PackagesConfig: pkgcataloging.DefaultConfig(),
}
}
func newPackageTaskFactory(catalogerFactory func(CatalogingFactoryConfig) pkg.Cataloger, tags ...string) packageTaskFactory {
func newPackageTaskFactory(catalogerFactory func(CatalogingFactoryConfig) pkg.Cataloger, tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return NewPackageTask(cfg, catalogerFactory(cfg), tags...)
}
}
func newSimplePackageTaskFactory(catalogerFactory func() pkg.Cataloger, tags ...string) packageTaskFactory {
func newSimplePackageTaskFactory(catalogerFactory func() pkg.Cataloger, tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return NewPackageTask(cfg, catalogerFactory(), tags...)
}
}
func (f PackageTaskFactories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error) {
var allTasks []Task
taskNames := strset.New()
duplicateTaskNames := strset.New()
var err error
for _, factory := range f {
tsk := factory(cfg)
if tsk == nil {
continue
}
tskName := tsk.Name()
if taskNames.Has(tskName) {
duplicateTaskNames.Add(tskName)
}
allTasks = append(allTasks, tsk)
taskNames.Add(tskName)
}
if duplicateTaskNames.Size() > 0 {
names := duplicateTaskNames.List()
sort.Strings(names)
err = fmt.Errorf("duplicate cataloger task names: %v", strings.Join(names, ", "))
}
return allTasks, err
}
// NewPackageTask creates a Task function for a generic pkg.Cataloger, honoring the common configuration options.
func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, sbom sbomsync.Builder) error {
+2 -2
View File
@@ -51,8 +51,8 @@ const (
)
//nolint:funlen
func DefaultPackageTaskFactories() PackageTaskFactories {
return []packageTaskFactory{
func DefaultPackageTaskFactories() Factories {
return []factory{
// OS package installed catalogers ///////////////////////////////////////////////////////////////////////////
newSimplePackageTaskFactory(arch.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "alpm", "archlinux"),
newSimplePackageTaskFactory(alpine.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "apk", "alpine"),
+152 -3
View File
@@ -3,17 +3,19 @@ package task
import (
"fmt"
"sort"
"strings"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/filecataloging"
)
// Selection represents the users request for a subset of tasks to run and the resulting set of task names that were
// selected. Additionally, all tokens that were matched on to reach the returned conclusion are also provided.
type Selection struct {
Request pkgcataloging.SelectionRequest
Request cataloging.SelectionRequest
Result *strset.Set
TokensByTask map[string]TokenSelection
}
@@ -52,7 +54,18 @@ func newSelection() Selection {
// Select parses the given expressions as two sets: expressions that represent a "set" operation, and expressions that
// represent all other operations. The parsed expressions are then evaluated against the given tasks to return
// a subset (or the same) set of tasks.
func Select(allTasks []Task, selectionRequest pkgcataloging.SelectionRequest) ([]Task, Selection, error) {
func Select(allTasks []Task, selectionRequest cataloging.SelectionRequest) ([]Task, Selection, error) {
ensureDefaultSelectionHasFiles(&selectionRequest, allTasks)
return _select(allTasks, selectionRequest)
}
func _select(allTasks []Task, selectionRequest cataloging.SelectionRequest) ([]Task, Selection, error) {
if selectionRequest.IsEmpty() {
selection := newSelection()
selection.Request = selectionRequest
return nil, selection, nil
}
nodes := newExpressionsFromSelectionRequest(newExpressionContext(allTasks), selectionRequest)
finalTasks, selection := selectByExpressions(allTasks, nodes)
@@ -62,6 +75,142 @@ func Select(allTasks []Task, selectionRequest pkgcataloging.SelectionRequest) ([
return finalTasks, selection, nodes.Validate()
}
// ensureDefaultSelectionHasFiles ensures that the default selection request has the "file" tag, as this is a required
// for backwards compatibility (when catalogers were only for packages and not for separate groups of tasks).
func ensureDefaultSelectionHasFiles(selectionRequest *cataloging.SelectionRequest, allTasks ...[]Task) {
for _, ts := range allTasks {
_, leftOver := tagsOrNamesThatTaskGroupRespondsTo(ts, strset.New(filecataloging.FileTag))
if leftOver.Has(filecataloging.FileTag) {
// the given set of tasks do not respond to file, so don't include it in the default selection
continue
}
defaultNamesOrTags := strset.New(selectionRequest.DefaultNamesOrTags...)
removals := strset.New(selectionRequest.RemoveNamesOrTags...)
missingFileIshTag := !defaultNamesOrTags.Has(filecataloging.FileTag) && !defaultNamesOrTags.Has("all") && !defaultNamesOrTags.Has("default")
if missingFileIshTag && !removals.Has(filecataloging.FileTag) {
log.Warnf("adding '%s' tag to the default cataloger selection, to override add '-%s' to the cataloger selection request", filecataloging.FileTag, filecataloging.FileTag)
selectionRequest.DefaultNamesOrTags = append(selectionRequest.DefaultNamesOrTags, filecataloging.FileTag)
}
}
}
// SelectInGroups is a convenience function that allows for selecting tasks from multiple groups of tasks. The original
// request is split into sub-requests, where only tokens that are relevant to the given group of tasks are considered.
// If tokens are passed that are not relevant to any group of tasks, an error is returned.
func SelectInGroups(taskGroups [][]Task, selectionRequest cataloging.SelectionRequest) ([][]Task, Selection, error) {
ensureDefaultSelectionHasFiles(&selectionRequest, taskGroups...)
reqs, errs := splitCatalogerSelectionRequest(selectionRequest, taskGroups)
if errs != nil {
return nil, Selection{
Request: selectionRequest,
}, errs
}
var finalTasks [][]Task
var selections []Selection
for idx, req := range reqs {
tskGroup := taskGroups[idx]
subFinalTasks, subSelection, err := _select(tskGroup, req)
if err != nil {
return nil, Selection{
Request: selectionRequest,
}, err
}
finalTasks = append(finalTasks, subFinalTasks)
selections = append(selections, subSelection)
}
return finalTasks, mergeSelections(selections, selectionRequest), nil
}
func mergeSelections(selections []Selection, ogRequest cataloging.SelectionRequest) Selection {
finalSelection := newSelection()
for _, s := range selections {
finalSelection.Result.Add(s.Result.List()...)
for name, tokenSelection := range s.TokensByTask {
if existing, exists := finalSelection.TokensByTask[name]; exists {
existing.merge(tokenSelection)
finalSelection.TokensByTask[name] = existing
} else {
finalSelection.TokensByTask[name] = tokenSelection
}
}
}
finalSelection.Request = ogRequest
return finalSelection
}
func splitCatalogerSelectionRequest(req cataloging.SelectionRequest, selectablePkgTaskGroups [][]Task) ([]cataloging.SelectionRequest, error) {
requestTagsOrNames := allRequestReferences(req)
leftoverTags := strset.New()
usedTagsAndNames := strset.New()
var usedTagGroups []*strset.Set
for _, taskGroup := range selectablePkgTaskGroups {
selectedTagOrNames, remainingTagsOrNames := tagsOrNamesThatTaskGroupRespondsTo(taskGroup, requestTagsOrNames)
leftoverTags = strset.Union(leftoverTags, remainingTagsOrNames)
usedTagGroups = append(usedTagGroups, selectedTagOrNames)
usedTagsAndNames.Add(selectedTagOrNames.List()...)
}
leftoverTags = strset.Difference(leftoverTags, usedTagsAndNames)
leftoverTags.Remove("all")
if leftoverTags.Size() > 0 {
l := leftoverTags.List()
sort.Strings(l)
return nil, fmt.Errorf("no cataloger tasks respond to the following selections: %v", strings.Join(l, ", "))
}
var newSelections []cataloging.SelectionRequest
for _, tags := range usedTagGroups {
newSelections = append(newSelections, newSelectionWithTags(req, tags))
}
return newSelections, nil
}
func newSelectionWithTags(req cataloging.SelectionRequest, tags *strset.Set) cataloging.SelectionRequest {
return cataloging.SelectionRequest{
DefaultNamesOrTags: filterTags(req.DefaultNamesOrTags, tags),
SubSelectTags: filterTags(req.SubSelectTags, tags),
AddNames: filterTags(req.AddNames, tags),
RemoveNamesOrTags: filterTags(req.RemoveNamesOrTags, tags),
}
}
func filterTags(reqTags []string, filterTags *strset.Set) []string {
var filtered []string
for _, tag := range reqTags {
if filterTags.Has(tag) {
filtered = append(filtered, tag)
}
}
return filtered
}
func tagsOrNamesThatTaskGroupRespondsTo(tasks []Task, requestTagsOrNames *strset.Set) (*strset.Set, *strset.Set) {
positiveRefs := strset.New()
for _, t := range tasks {
if sel, ok := t.(Selector); ok {
positiveRefs.Add("all") // everything responds to "all"
positiveRefs.Add(strset.Intersection(requestTagsOrNames, strset.New(sel.Selectors()...)).List()...)
}
positiveRefs.Add(t.Name())
}
return positiveRefs, strset.Difference(requestTagsOrNames, positiveRefs)
}
func allRequestReferences(s cataloging.SelectionRequest) *strset.Set {
st := strset.New()
st.Add(s.DefaultNamesOrTags...)
st.Add(s.SubSelectTags...)
st.Add(s.AddNames...)
st.Add(s.RemoveNamesOrTags...)
return st
}
// selectByExpressions the set of tasks to run based on the given expression(s).
func selectByExpressions(ts tasks, nodes Expressions) (tasks, Selection) {
if len(nodes) == 0 {
+459 -41
View File
@@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/file"
)
@@ -21,45 +21,54 @@ func dummyTask(name string, tags ...string) Task {
}
// note: this test fixture does not need to be kept up to date here, but makes a great test subject
func createDummyTasks() tasks {
func createDummyPackageTasks() tasks {
return []Task{
// OS package installed catalogers
dummyTask("alpm-db-cataloger", "directory", "installed", "image", "os", "alpm", "archlinux"),
dummyTask("apk-db-cataloger", "directory", "installed", "image", "os", "apk", "alpine"),
dummyTask("dpkg-db-cataloger", "directory", "installed", "image", "os", "dpkg", "debian"),
dummyTask("portage-cataloger", "directory", "installed", "image", "os", "portage", "gentoo"),
dummyTask("rpm-db-cataloger", "directory", "installed", "image", "os", "rpm", "redhat"),
dummyTask("alpm-db-cataloger", "package", "directory", "installed", "image", "os", "alpm", "archlinux"),
dummyTask("apk-db-cataloger", "package", "directory", "installed", "image", "os", "apk", "alpine"),
dummyTask("dpkg-db-cataloger", "package", "directory", "installed", "image", "os", "dpkg", "debian"),
dummyTask("portage-cataloger", "package", "directory", "installed", "image", "os", "portage", "gentoo"),
dummyTask("rpm-db-cataloger", "package", "directory", "installed", "image", "os", "rpm", "redhat"),
// OS package declared catalogers
dummyTask("rpm-archive-cataloger", "declared", "directory", "os", "rpm", "redhat"),
dummyTask("rpm-archive-cataloger", "package", "declared", "directory", "os", "rpm", "redhat"),
// language-specific package installed catalogers
dummyTask("conan-info-cataloger", "installed", "image", "language", "cpp", "conan"),
dummyTask("javascript-package-cataloger", "installed", "image", "language", "javascript", "node"),
dummyTask("php-composer-installed-cataloger", "installed", "image", "language", "php", "composer"),
dummyTask("ruby-installed-gemspec-cataloger", "installed", "image", "language", "ruby", "gem", "gemspec"),
dummyTask("rust-cargo-lock-cataloger", "installed", "image", "language", "rust", "binary"),
dummyTask("conan-info-cataloger", "package", "installed", "image", "language", "cpp", "conan"),
dummyTask("javascript-package-cataloger", "package", "installed", "image", "language", "javascript", "node"),
dummyTask("php-composer-installed-cataloger", "package", "installed", "image", "language", "php", "composer"),
dummyTask("ruby-installed-gemspec-cataloger", "package", "installed", "image", "language", "ruby", "gem", "gemspec"),
dummyTask("rust-cargo-lock-cataloger", "package", "installed", "image", "language", "rust", "binary"),
// language-specific package declared catalogers
dummyTask("conan-cataloger", "declared", "directory", "language", "cpp", "conan"),
dummyTask("dart-pubspec-lock-cataloger", "declared", "directory", "language", "dart"),
dummyTask("dotnet-deps-cataloger", "declared", "directory", "language", "dotnet", "c#"),
dummyTask("elixir-mix-lock-cataloger", "declared", "directory", "language", "elixir"),
dummyTask("erlang-rebar-lock-cataloger", "declared", "directory", "language", "erlang"),
dummyTask("javascript-lock-cataloger", "declared", "directory", "language", "javascript", "node", "npm"),
dummyTask("conan-cataloger", "package", "declared", "directory", "language", "cpp", "conan"),
dummyTask("dart-pubspec-lock-cataloger", "package", "declared", "directory", "language", "dart"),
dummyTask("dotnet-deps-cataloger", "package", "declared", "directory", "language", "dotnet", "c#"),
dummyTask("elixir-mix-lock-cataloger", "package", "declared", "directory", "language", "elixir"),
dummyTask("erlang-rebar-lock-cataloger", "package", "declared", "directory", "language", "erlang"),
dummyTask("javascript-lock-cataloger", "package", "declared", "directory", "language", "javascript", "node", "npm"),
// language-specific package for both image and directory scans (but not necessarily declared)
dummyTask("dotnet-portable-executable-cataloger", "directory", "installed", "image", "language", "dotnet", "c#"),
dummyTask("python-installed-package-cataloger", "directory", "installed", "image", "language", "python"),
dummyTask("go-module-binary-cataloger", "directory", "installed", "image", "language", "go", "golang", "gomod", "binary"),
dummyTask("java-archive-cataloger", "directory", "installed", "image", "language", "java", "maven"),
dummyTask("graalvm-native-image-cataloger", "directory", "installed", "image", "language", "java"),
dummyTask("dotnet-portable-executable-cataloger", "package", "directory", "installed", "image", "language", "dotnet", "c#"),
dummyTask("python-installed-package-cataloger", "package", "directory", "installed", "image", "language", "python"),
dummyTask("go-module-binary-cataloger", "package", "directory", "installed", "image", "language", "go", "golang", "gomod", "binary"),
dummyTask("java-archive-cataloger", "package", "directory", "installed", "image", "language", "java", "maven"),
dummyTask("graalvm-native-image-cataloger", "package", "directory", "installed", "image", "language", "java"),
// other package catalogers
dummyTask("binary-cataloger", "declared", "directory", "image", "binary"),
dummyTask("github-actions-usage-cataloger", "declared", "directory", "github", "github-actions"),
dummyTask("github-action-workflow-usage-cataloger", "declared", "directory", "github", "github-actions"),
dummyTask("sbom-cataloger", "declared", "directory", "image", "sbom"),
dummyTask("binary-cataloger", "package", "declared", "directory", "image", "binary"),
dummyTask("github-actions-usage-cataloger", "package", "declared", "directory", "github", "github-actions"),
dummyTask("github-action-workflow-usage-cataloger", "package", "declared", "directory", "github", "github-actions"),
dummyTask("sbom-cataloger", "package", "declared", "directory", "image", "sbom"),
}
}
func createDummyFileTasks() tasks {
return []Task{
dummyTask("file-content-cataloger", "file", "content"),
dummyTask("file-metadata-cataloger", "file", "metadata"),
dummyTask("file-digest-cataloger", "file", "digest"),
dummyTask("file-executable-cataloger", "file", "binary-metadata"),
}
}
@@ -72,7 +81,7 @@ func TestSelect(t *testing.T) {
expressions []string
wantNames []string
wantTokens map[string]TokenSelection
wantRequest pkgcataloging.SelectionRequest
wantRequest cataloging.SelectionRequest
wantErr assert.ErrorAssertionFunc
}{
{
@@ -82,11 +91,11 @@ func TestSelect(t *testing.T) {
expressions: []string{},
wantNames: []string{},
wantTokens: map[string]TokenSelection{},
wantRequest: pkgcataloging.SelectionRequest{},
wantRequest: cataloging.SelectionRequest{},
},
{
name: "use default tasks",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"image",
},
@@ -129,13 +138,13 @@ func TestSelect(t *testing.T) {
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
},
},
{
name: "select, add, and remove tasks",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"image",
},
@@ -175,7 +184,7 @@ func TestSelect(t *testing.T) {
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
SubSelectTags: []string{"os"},
RemoveNamesOrTags: []string{"dpkg"},
@@ -184,7 +193,7 @@ func TestSelect(t *testing.T) {
},
{
name: "allow for partial selections",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"image",
},
@@ -228,7 +237,7 @@ func TestSelect(t *testing.T) {
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
SubSelectTags: []string{"os", "rust-cargo-lock-cataloger"},
RemoveNamesOrTags: []string{"dpkg"},
@@ -238,7 +247,7 @@ func TestSelect(t *testing.T) {
},
{
name: "select all tasks",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"all",
},
@@ -299,13 +308,13 @@ func TestSelect(t *testing.T) {
"github-action-workflow-usage-cataloger": newTokenSelection([]string{"all"}, nil),
"sbom-cataloger": newTokenSelection([]string{"all"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"all"},
},
},
{
name: "set default with multiple tags",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"gemspec",
"python",
@@ -319,10 +328,31 @@ func TestSelect(t *testing.T) {
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"gemspec"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"python"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"gemspec", "python"},
},
},
{
name: "automatically add file to default tags",
allTasks: createDummyFileTasks(),
basis: []string{},
expressions: []string{},
wantNames: []string{
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
wantTokens: map[string]TokenSelection{
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"file"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -330,7 +360,7 @@ func TestSelect(t *testing.T) {
tt.wantErr = assert.NoError
}
req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
req := cataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
got, gotEvidence, err := Select(tt.allTasks, req)
tt.wantErr(t, err)
@@ -367,3 +397,391 @@ func TestSelect(t *testing.T) {
})
}
}
func TestSelectInGroups(t *testing.T) {
tests := []struct {
name string
taskGroups [][]Task
selectionReq cataloging.SelectionRequest
wantGroups [][]string
wantTokens map[string]TokenSelection
wantRequest cataloging.SelectionRequest
wantErr assert.ErrorAssertionFunc
}{
{
name: "select only within the file tasks (leave package tasks alone)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().
WithDefaults("image"). // note: file missing
WithSubSelections("content", "digest"),
wantGroups: [][]string{
{
// this is the original, untouched package task list
"alpm-db-cataloger",
"apk-db-cataloger",
"dpkg-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
"conan-info-cataloger",
"javascript-package-cataloger",
"php-composer-installed-cataloger",
"ruby-installed-gemspec-cataloger",
"rust-cargo-lock-cataloger",
"dotnet-portable-executable-cataloger",
"python-installed-package-cataloger",
"go-module-binary-cataloger",
"java-archive-cataloger",
"graalvm-native-image-cataloger",
"binary-cataloger",
"sbom-cataloger",
},
{
// this has been filtered based on the request
"file-content-cataloger",
"file-digest-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages
"alpm-db-cataloger": newTokenSelection([]string{"image"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"image"}, nil),
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"image"}, nil),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
"portage-cataloger": newTokenSelection([]string{"image"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
"rpm-db-cataloger": newTokenSelection([]string{"image"}, nil),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
// files
"file-content-cataloger": newTokenSelection([]string{"content", "file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"digest", "file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"}, // note: file automatically added
SubSelectTags: []string{"content", "digest"},
},
wantErr: assert.NoError,
},
{
name: "select package tasks (leave file tasks alone)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("image").WithSubSelections("os"),
wantGroups: [][]string{
{
// filtered based on the request
"alpm-db-cataloger",
"apk-db-cataloger",
"dpkg-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
},
{
// this is the original, untouched file task list
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages - os
"alpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil),
"rpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"portage-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
// packages - remaining
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
// files
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
SubSelectTags: []string{"os"},
},
wantErr: assert.NoError,
},
{
name: "select only file tasks (default)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("file"),
wantGroups: [][]string{
// filtered based on the request
nil,
{
// this is the original, untouched file task list
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// files
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"file"},
},
wantErr: assert.NoError,
},
{
name: "select only file tasks (via removal of package)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("file", "image").WithRemovals("package"),
wantGroups: [][]string{
// filtered based on the request
nil,
{
// this is the original, untouched file task list
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages
"alpm-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"apk-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"binary-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"conan-info-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"dpkg-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"java-archive-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"portage-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"rpm-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"sbom-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"rpm-archive-cataloger": newTokenSelection(nil, []string{"package"}),
"conan-cataloger": newTokenSelection(nil, []string{"package"}),
"dart-pubspec-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"dotnet-deps-cataloger": newTokenSelection(nil, []string{"package"}),
"elixir-mix-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"erlang-rebar-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"javascript-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"github-actions-usage-cataloger": newTokenSelection(nil, []string{"package"}),
"github-action-workflow-usage-cataloger": newTokenSelection(nil, []string{"package"}),
// files
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"file", "image"},
RemoveNamesOrTags: []string{"package"},
},
wantErr: assert.NoError,
},
{
name: "select file and package tasks",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().
WithDefaults("image").
WithSubSelections("os", "content", "digest"),
wantGroups: [][]string{
{
// filtered based on the request
"alpm-db-cataloger",
"apk-db-cataloger",
"dpkg-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
},
{
// filtered based on the request
"file-content-cataloger",
"file-digest-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages - os
"alpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil),
"rpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"portage-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
// packages - remaining
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
// files
"file-content-cataloger": newTokenSelection([]string{"file", "content"}, nil), // note extra tags
"file-digest-cataloger": newTokenSelection([]string{"file", "digest"}, nil), // note extra tags
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
SubSelectTags: []string{"os", "content", "digest"},
},
wantErr: assert.NoError,
},
{
name: "complex selection with multiple operators across groups",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().
WithDefaults("os"). // note: no file tag present
WithExpression("+github-actions-usage-cataloger", "-dpkg", "-digest", "content", "+file-metadata-cataloger", "-declared"),
wantGroups: [][]string{
{
"alpm-db-cataloger",
"apk-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
"github-actions-usage-cataloger",
},
{
"file-content-cataloger",
"file-metadata-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// selected package tasks
"alpm-db-cataloger": newTokenSelection([]string{"os"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"os"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"os"}, []string{"dpkg"}),
"portage-cataloger": newTokenSelection([]string{"os"}, nil),
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, []string{"declared"}),
"rpm-db-cataloger": newTokenSelection([]string{"os"}, nil),
"github-actions-usage-cataloger": newTokenSelection([]string{"github-actions-usage-cataloger"}, []string{"declared"}),
// selected file tasks
"file-content-cataloger": newTokenSelection([]string{"content", "file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file-metadata-cataloger", "file"}, nil),
// removed package tasks
"binary-cataloger": newTokenSelection(nil, []string{"declared"}),
"conan-cataloger": newTokenSelection(nil, []string{"declared"}),
"dart-pubspec-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"dotnet-deps-cataloger": newTokenSelection(nil, []string{"declared"}),
"elixir-mix-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"erlang-rebar-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"github-action-workflow-usage-cataloger": newTokenSelection(nil, []string{"declared"}),
"javascript-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"sbom-cataloger": newTokenSelection(nil, []string{"declared"}),
// removed file tasks
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, []string{"digest"}),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"os", "file"}, // note: file added automatically
SubSelectTags: []string{"content"},
RemoveNamesOrTags: []string{"dpkg", "digest", "declared"},
AddNames: []string{"github-actions-usage-cataloger", "file-metadata-cataloger"},
},
wantErr: assert.NoError,
},
{
name: "invalid tag",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("invalid"),
wantGroups: nil,
wantTokens: nil,
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"invalid", "file"},
},
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = assert.NoError
}
gotGroups, gotSelection, err := SelectInGroups(tt.taskGroups, tt.selectionReq)
tt.wantErr(t, err)
if err != nil {
// dev note: this is useful for debugging when needed...
//for _, e := range gotEvidence.Request.Expressions {
// t.Logf("expression (errors %q): %#v", e.Errors, e)
//}
// note: we DON'T bail early in validations... this is because we should always return the full set of
// of selected tasks and surrounding evidence.
}
var gotGroupNames [][]string
for _, group := range gotGroups {
var names []string
for _, task := range group {
names = append(names, task.Name())
}
gotGroupNames = append(gotGroupNames, names)
}
assert.Equal(t, tt.wantGroups, gotGroupNames)
assert.Equal(t, tt.wantTokens, gotSelection.TokensByTask)
assert.Equal(t, tt.wantRequest, gotSelection.Request)
})
}
}