mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 05:52:25 +01:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
297ece6904 | ||
|
|
5602c80edb | ||
|
|
fe271e89f6 | ||
|
|
0409eef615 | ||
|
|
bdda3481c7 | ||
|
|
6f4fb61a1c | ||
|
|
313d9212b6 | ||
|
|
fb2b54a6dc |
+1
-1
@@ -90,7 +90,7 @@ tools:
|
|||||||
|
|
||||||
- name: gh
|
- name: gh
|
||||||
version:
|
version:
|
||||||
want: v2.42.0
|
want: v2.42.1
|
||||||
method: github-release
|
method: github-release
|
||||||
with:
|
with:
|
||||||
repo: cli/cli
|
repo: cli/cli
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
- name: Restore base benchmark result
|
- name: Restore base benchmark result
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: test/results/benchmark-main.txt
|
path: test/results/benchmark-main.txt
|
||||||
# use base sha for PR or new commit hash for main push in benchmark result key
|
# use base sha for PR or new commit hash for main push in benchmark result key
|
||||||
|
|||||||
@@ -37,31 +37,31 @@ jobs:
|
|||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
- name: Restore Java test-fixture cache
|
- name: Restore Java test-fixture cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages
|
path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages
|
||||||
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore RPM test-fixture cache
|
- name: Restore RPM test-fixture cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/redhat/test-fixtures/rpms
|
path: syft/pkg/cataloger/redhat/test-fixtures/rpms
|
||||||
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/redhat/test-fixtures/rpms.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/redhat/test-fixtures/rpms.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore go binary test-fixture cache
|
- name: Restore go binary test-fixture cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries
|
path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries
|
||||||
key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore binary cataloger test-fixture cache
|
- name: Restore binary cataloger test-fixture cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/binary/test-fixtures/classifiers/bin
|
path: syft/pkg/cataloger/binary/test-fixtures/classifiers/bin
|
||||||
key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore Kernel test-fixture cache
|
- name: Restore Kernel test-fixture cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/kernel/test-fixtures/cache
|
path: syft/pkg/cataloger/kernel/test-fixtures/cache
|
||||||
key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }}
|
||||||
@@ -84,7 +84,7 @@ jobs:
|
|||||||
run: make validate-cyclonedx-schema
|
run: make validate-cyclonedx-schema
|
||||||
|
|
||||||
- name: Restore integration test cache
|
- name: Restore integration test cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/test/integration/test-fixtures/cache
|
path: ${{ github.workspace }}/test/integration/test-fixtures/cache
|
||||||
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }}
|
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }}
|
||||||
@@ -110,7 +110,7 @@ jobs:
|
|||||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||||
- name: Upload snapshot artifacts
|
- name: Upload snapshot artifacts
|
||||||
uses: actions/cache/save@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache/save@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
||||||
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
||||||
@@ -134,7 +134,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
id: snapshot-cache
|
id: snapshot-cache
|
||||||
uses: actions/cache/restore@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
snapshot
|
snapshot
|
||||||
@@ -152,7 +152,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Restore install.sh test image cache
|
- name: Restore install.sh test image cache
|
||||||
id: install-test-image-cache
|
id: install-test-image-cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/test/install/cache
|
path: ${{ github.workspace }}/test/install/cache
|
||||||
key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }}
|
key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }}
|
||||||
@@ -187,7 +187,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
id: snapshot-cache
|
id: snapshot-cache
|
||||||
uses: actions/cache/restore@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
snapshot
|
snapshot
|
||||||
@@ -202,7 +202,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Restore docker image cache for compare testing
|
- name: Restore docker image cache for compare testing
|
||||||
id: mac-compare-testing-cache
|
id: mac-compare-testing-cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: image.tar
|
path: image.tar
|
||||||
key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }}
|
key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }}
|
||||||
@@ -226,14 +226,14 @@ jobs:
|
|||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
- name: Restore CLI test-fixture cache
|
- name: Restore CLI test-fixture cache
|
||||||
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/test/cli/test-fixtures/cache
|
path: ${{ github.workspace }}/test/cli/test-fixtures/cache
|
||||||
key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }}
|
key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }}
|
||||||
|
|
||||||
- name: Download snapshot build
|
- name: Download snapshot build
|
||||||
id: snapshot-cache
|
id: snapshot-cache
|
||||||
uses: actions/cache/restore@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
|
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
snapshot
|
snapshot
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ func create(id clio.Identification, out io.Writer) (clio.Application, *cobra.Com
|
|||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
scanCmd,
|
scanCmd,
|
||||||
commands.Packages(app, scanCmd), // this is currently an alias for the scan command
|
commands.Packages(app, scanCmd), // this is currently an alias for the scan command
|
||||||
|
commands.Cataloger(app),
|
||||||
commands.Attest(app),
|
commands.Attest(app),
|
||||||
commands.Convert(app),
|
commands.Convert(app),
|
||||||
clio.VersionCommand(id),
|
clio.VersionCommand(id),
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry
|
attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry
|
||||||
`
|
`
|
||||||
attestSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp
|
attestSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp
|
||||||
attestHelp = attestExample + attestSchemeHelp
|
attestHelp = attestExample + attestSchemeHelp
|
||||||
@@ -232,7 +232,7 @@ func attestCommand(sbomFilepath string, opts *attestOptions, userInput string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func predicateType(outputName string) string {
|
func predicateType(outputName string) string {
|
||||||
// Select Cosign predicate type based on defined output type
|
// select the Cosign predicate type based on defined output type
|
||||||
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
|
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
|
||||||
switch strings.ToLower(outputName) {
|
switch strings.ToLower(outputName) {
|
||||||
case "cyclonedx-json":
|
case "cyclonedx-json":
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Cataloger(app clio.Application) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "cataloger",
|
||||||
|
Short: "Show available catalogers and configuration",
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.AddCommand(
|
||||||
|
CatalogerList(app),
|
||||||
|
)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/scylladb/go-set/strset"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/syft/internal/bus"
|
||||||
|
"github.com/anchore/syft/internal/task"
|
||||||
|
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||||
|
)
|
||||||
|
|
||||||
|
type catalogerListOptions struct {
|
||||||
|
Output string `yaml:"output" json:"output" mapstructure:"output"`
|
||||||
|
DefaultCatalogers []string `yaml:"default-catalogers" json:"default-catalogers" mapstructure:"default-catalogers"`
|
||||||
|
SelectCatalogers []string `yaml:"select-catalogers" json:"select-catalogers" mapstructure:"select-catalogers"`
|
||||||
|
ShowHidden bool `yaml:"show-hidden" json:"show-hidden" mapstructure:"show-hidden"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *catalogerListOptions) AddFlags(flags clio.FlagSet) {
|
||||||
|
flags.StringVarP(&o.Output, "output", "o", "format to output the cataloger list (available: table, json)")
|
||||||
|
|
||||||
|
flags.StringArrayVarP(&o.DefaultCatalogers, "override-default-catalogers", "", "override the default catalogers with an expression")
|
||||||
|
|
||||||
|
flags.StringArrayVarP(&o.SelectCatalogers, "select-catalogers", "", "select catalogers with an expression")
|
||||||
|
|
||||||
|
flags.BoolVarP(&o.ShowHidden, "show-hidden", "s", "show catalogers that have been de-selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultCatalogerListOptions() *catalogerListOptions {
|
||||||
|
return &catalogerListOptions{
|
||||||
|
DefaultCatalogers: []string{"all"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CatalogerList(app clio.Application) *cobra.Command {
|
||||||
|
opts := defaultCatalogerListOptions()
|
||||||
|
|
||||||
|
return app.SetupCommand(&cobra.Command{
|
||||||
|
Use: "list [OPTIONS]",
|
||||||
|
Short: "List available catalogers",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runCatalogerList(opts)
|
||||||
|
},
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCatalogerList(opts *catalogerListOptions) error {
|
||||||
|
factories := task.DefaultPackageTaskFactories()
|
||||||
|
allTasks, err := factories.Tasks(task.DefaultCatalogingFactoryConfig())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create cataloger tasks: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
report, err := catalogerListReport(opts, allTasks)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to generate cataloger list report: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.Report(report)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (string, error) {
|
||||||
|
selectedTasks, selectionEvidence, err := task.Select(allTasks,
|
||||||
|
pkgcataloging.NewSelectionRequest().
|
||||||
|
WithDefaults(opts.DefaultCatalogers...).
|
||||||
|
WithExpression(opts.SelectCatalogers...),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to select catalogers: %w", err)
|
||||||
|
}
|
||||||
|
var report string
|
||||||
|
|
||||||
|
switch opts.Output {
|
||||||
|
case "json":
|
||||||
|
report, err = renderCatalogerListJSON(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
|
||||||
|
case "table", "":
|
||||||
|
if opts.ShowHidden {
|
||||||
|
report = renderCatalogerListTable(allTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
|
||||||
|
} else {
|
||||||
|
report = renderCatalogerListTable(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to render cataloger list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return report, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) (string, error) {
|
||||||
|
type node struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
names, tagsByName := extractTaskInfo(tasks)
|
||||||
|
|
||||||
|
nodesByName := make(map[string]node)
|
||||||
|
|
||||||
|
for name := range tagsByName {
|
||||||
|
tagsSelected := selection.TokensByTask[name].SelectedOn.List()
|
||||||
|
|
||||||
|
if len(tagsSelected) == 1 && tagsSelected[0] == "all" {
|
||||||
|
tagsSelected = tagsByName[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(tagsSelected)
|
||||||
|
|
||||||
|
if tagsSelected == nil {
|
||||||
|
// ensure collections are not null
|
||||||
|
tagsSelected = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodesByName[name] = node{
|
||||||
|
Name: name,
|
||||||
|
Tags: tagsSelected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type document struct {
|
||||||
|
DefaultSelection []string `json:"default"`
|
||||||
|
Selection []string `json:"selection"`
|
||||||
|
Catalogers []node `json:"catalogers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if selections == nil {
|
||||||
|
// ensure collections are not null
|
||||||
|
selections = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc := document{
|
||||||
|
DefaultSelection: defaultSelections,
|
||||||
|
Selection: selections,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
doc.Catalogers = append(doc.Catalogers, nodesByName[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
by, err := json.Marshal(doc)
|
||||||
|
|
||||||
|
return string(by), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderCatalogerListTable(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) string {
|
||||||
|
t := table.NewWriter()
|
||||||
|
t.SetStyle(table.StyleLight)
|
||||||
|
t.AppendHeader(table.Row{"Cataloger", "Tags"})
|
||||||
|
|
||||||
|
names, tagsByName := extractTaskInfo(tasks)
|
||||||
|
|
||||||
|
rowsByName := make(map[string]table.Row)
|
||||||
|
|
||||||
|
for name, tags := range tagsByName {
|
||||||
|
rowsByName[name] = formatRow(name, tags, selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
t.AppendRow(rowsByName[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
report := t.Render()
|
||||||
|
|
||||||
|
if len(selections) > 0 {
|
||||||
|
header := "Selected by expressions:\n"
|
||||||
|
for _, expr := range selections {
|
||||||
|
header += fmt.Sprintf(" - %q\n", expr)
|
||||||
|
}
|
||||||
|
report = header + report
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(defaultSelections) > 0 {
|
||||||
|
header := "Default selections:\n"
|
||||||
|
for _, expr := range defaultSelections {
|
||||||
|
header += fmt.Sprintf(" - %q\n", expr)
|
||||||
|
}
|
||||||
|
report = header + report
|
||||||
|
}
|
||||||
|
|
||||||
|
return report
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatRow(name string, tags []string, selection task.Selection) table.Row {
|
||||||
|
isIncluded := selection.Result.Has(name)
|
||||||
|
var selections *task.TokenSelection
|
||||||
|
if s, exists := selection.TokensByTask[name]; exists {
|
||||||
|
selections = &s
|
||||||
|
}
|
||||||
|
|
||||||
|
var formattedTags []string
|
||||||
|
for _, tag := range tags {
|
||||||
|
formattedTags = append(formattedTags, formatToken(tag, selections, isIncluded))
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagStr string
|
||||||
|
if isIncluded {
|
||||||
|
tagStr = strings.Join(formattedTags, ", ")
|
||||||
|
} else {
|
||||||
|
tagStr = strings.Join(formattedTags, grey.Render(", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: selection should keep warnings (non-selections) in struct
|
||||||
|
|
||||||
|
return table.Row{
|
||||||
|
formatToken(name, selections, isIncluded),
|
||||||
|
tagStr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
green = lipgloss.NewStyle().Foreground(lipgloss.Color("10")) // hi green
|
||||||
|
grey = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) // dark grey
|
||||||
|
red = lipgloss.NewStyle().Foreground(lipgloss.Color("9")) // high red
|
||||||
|
)
|
||||||
|
|
||||||
|
func formatToken(token string, selection *task.TokenSelection, included bool) string {
|
||||||
|
if included && selection != nil {
|
||||||
|
// format all tokens in selection in green
|
||||||
|
if selection.SelectedOn.Has(token) {
|
||||||
|
return green.Render(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
// format all tokens in selection in red, all others in grey
|
||||||
|
if selection != nil && selection.DeselectedOn.Has(token) {
|
||||||
|
return red.Render(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
return grey.Render(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTaskInfo(tasks []task.Task) ([]string, map[string][]string) {
|
||||||
|
tagsByName := make(map[string][]string)
|
||||||
|
var names []string
|
||||||
|
|
||||||
|
for _, tsk := range tasks {
|
||||||
|
var tags []string
|
||||||
|
name := tsk.Name()
|
||||||
|
|
||||||
|
if s, ok := tsk.(task.Selector); ok {
|
||||||
|
set := strset.New(s.Selectors()...)
|
||||||
|
set.Remove(name)
|
||||||
|
tags = set.List()
|
||||||
|
sort.Strings(tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsByName[name] = tags
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(names)
|
||||||
|
|
||||||
|
return names, tagsByName
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/scylladb/go-set/strset"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/sbomsync"
|
||||||
|
"github.com/anchore/syft/internal/task"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
task.Task
|
||||||
|
task.Selector
|
||||||
|
} = (*dummyTask)(nil)
|
||||||
|
|
||||||
|
type dummyTask struct {
|
||||||
|
name string
|
||||||
|
selectors []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummyTask) HasAllSelectors(s ...string) bool {
|
||||||
|
return strset.New(d.selectors...).Has(s...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummyTask) Selectors() []string {
|
||||||
|
return d.selectors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummyTask) Name() string {
|
||||||
|
return d.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummyTask) Execute(_ context.Context, _ file.Resolver, _ sbomsync.Builder) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTasks() []task.Task {
|
||||||
|
return []task.Task{
|
||||||
|
dummyTask{
|
||||||
|
name: "task1",
|
||||||
|
selectors: []string{"image", "a", "b", "1"},
|
||||||
|
},
|
||||||
|
dummyTask{
|
||||||
|
name: "task2",
|
||||||
|
selectors: []string{"image", "b", "c", "2"},
|
||||||
|
},
|
||||||
|
dummyTask{
|
||||||
|
name: "task3",
|
||||||
|
selectors: []string{"directory", "c", "d", "3"},
|
||||||
|
},
|
||||||
|
dummyTask{
|
||||||
|
name: "task4",
|
||||||
|
selectors: []string{"directory", "d", "e", "4"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_catalogerListReport(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
options *catalogerListOptions
|
||||||
|
want string
|
||||||
|
wantErr require.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no expressions, table",
|
||||||
|
options: func() *catalogerListOptions {
|
||||||
|
c := defaultCatalogerListOptions()
|
||||||
|
c.Output = "table"
|
||||||
|
return c
|
||||||
|
}(),
|
||||||
|
want: `
|
||||||
|
Default selections:
|
||||||
|
- "all"
|
||||||
|
┌───────────┬────────────────────┐
|
||||||
|
│ CATALOGER │ TAGS │
|
||||||
|
├───────────┼────────────────────┤
|
||||||
|
│ task1 │ 1, a, b, image │
|
||||||
|
│ task2 │ 2, b, c, image │
|
||||||
|
│ task3 │ 3, c, d, directory │
|
||||||
|
│ task4 │ 4, d, directory, e │
|
||||||
|
└───────────┴────────────────────┘
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no expressions, json",
|
||||||
|
options: func() *catalogerListOptions {
|
||||||
|
c := defaultCatalogerListOptions()
|
||||||
|
c.Output = "json"
|
||||||
|
return c
|
||||||
|
}(),
|
||||||
|
want: `
|
||||||
|
{"default":["all"],"selection":[],"catalogers":[{"name":"task1","tags":["1","a","b","image"]},{"name":"task2","tags":["2","b","c","image"]},{"name":"task3","tags":["3","c","d","directory"]},{"name":"task4","tags":["4","d","directory","e"]}]}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no expressions, default selection, table",
|
||||||
|
options: func() *catalogerListOptions {
|
||||||
|
c := defaultCatalogerListOptions()
|
||||||
|
c.Output = "table"
|
||||||
|
c.DefaultCatalogers = []string{
|
||||||
|
"image",
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}(),
|
||||||
|
want: `
|
||||||
|
Default selections:
|
||||||
|
- "image"
|
||||||
|
┌───────────┬────────────────┐
|
||||||
|
│ CATALOGER │ TAGS │
|
||||||
|
├───────────┼────────────────┤
|
||||||
|
│ task1 │ 1, a, b, image │
|
||||||
|
│ task2 │ 2, b, c, image │
|
||||||
|
└───────────┴────────────────┘
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no expressions, default selection, json",
|
||||||
|
options: func() *catalogerListOptions {
|
||||||
|
c := defaultCatalogerListOptions()
|
||||||
|
c.Output = "json"
|
||||||
|
c.DefaultCatalogers = []string{
|
||||||
|
"image",
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}(),
|
||||||
|
want: `
|
||||||
|
{"default":["image"],"selection":[],"catalogers":[{"name":"task1","tags":["image"]},{"name":"task2","tags":["image"]}]}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with expressions, default selection, table",
|
||||||
|
options: func() *catalogerListOptions {
|
||||||
|
c := defaultCatalogerListOptions()
|
||||||
|
c.Output = "table"
|
||||||
|
c.DefaultCatalogers = []string{
|
||||||
|
"image",
|
||||||
|
}
|
||||||
|
c.SelectCatalogers = []string{
|
||||||
|
"-directory",
|
||||||
|
"+task3",
|
||||||
|
"-c",
|
||||||
|
"b",
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}(),
|
||||||
|
want: `
|
||||||
|
Default selections:
|
||||||
|
- "image"
|
||||||
|
Selected by expressions:
|
||||||
|
- "-directory"
|
||||||
|
- "+task3"
|
||||||
|
- "-c"
|
||||||
|
- "b"
|
||||||
|
┌───────────┬────────────────────┐
|
||||||
|
│ CATALOGER │ TAGS │
|
||||||
|
├───────────┼────────────────────┤
|
||||||
|
│ task1 │ 1, a, b, image │
|
||||||
|
│ task3 │ 3, c, d, directory │
|
||||||
|
└───────────┴────────────────────┘
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with expressions, default selection, json",
|
||||||
|
options: func() *catalogerListOptions {
|
||||||
|
c := defaultCatalogerListOptions()
|
||||||
|
c.Output = "json"
|
||||||
|
c.DefaultCatalogers = []string{
|
||||||
|
"image",
|
||||||
|
}
|
||||||
|
c.SelectCatalogers = []string{
|
||||||
|
"-directory",
|
||||||
|
"+task3",
|
||||||
|
"-c",
|
||||||
|
"b",
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}(),
|
||||||
|
want: `
|
||||||
|
{"default":["image"],"selection":["-directory","+task3","-c","b"],"catalogers":[{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.wantErr == nil {
|
||||||
|
tt.wantErr = require.NoError
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := catalogerListReport(tt.options, testTasks())
|
||||||
|
tt.wantErr(t, err)
|
||||||
|
assert.Equal(t, strings.TrimSpace(tt.want), strings.TrimSpace(got))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/clio"
|
"github.com/anchore/clio"
|
||||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
"github.com/anchore/syft/cmd/syft/internal/ui"
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Packages(app clio.Application, scanCmd *cobra.Command) *cobra.Command {
|
func Packages(app clio.Application, scanCmd *cobra.Command) *cobra.Command {
|
||||||
@@ -13,11 +14,14 @@ func Packages(app clio.Application, scanCmd *cobra.Command) *cobra.Command {
|
|||||||
opts := defaultScanOptions()
|
opts := defaultScanOptions()
|
||||||
|
|
||||||
cmd := app.SetupCommand(&cobra.Command{
|
cmd := app.SetupCommand(&cobra.Command{
|
||||||
Use: "packages [SOURCE]",
|
Use: "packages [SOURCE]",
|
||||||
Short: scanCmd.Short,
|
Short: scanCmd.Short,
|
||||||
Long: scanCmd.Long,
|
Long: scanCmd.Long,
|
||||||
Args: scanCmd.Args,
|
Args: scanCmd.Args,
|
||||||
Example: scanCmd.Example,
|
Example: internal.Tprintf(scanHelp, map[string]interface{}{
|
||||||
|
"appName": id.Name,
|
||||||
|
"command": "packages",
|
||||||
|
}),
|
||||||
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
restoreStdout := ui.CaptureStdoutToTraceLog()
|
restoreStdout := ui.CaptureStdoutToTraceLog()
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ require (
|
|||||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||||
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501
|
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501
|
||||||
github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54
|
github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||||
// we are hinting brotli to latest due to warning when installing archiver v3:
|
// we are hinting brotli to latest due to warning when installing archiver v3:
|
||||||
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
|
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
|
||||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
||||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||||
|
github.com/charmbracelet/bubbles v0.17.1
|
||||||
github.com/charmbracelet/bubbletea v0.25.0
|
github.com/charmbracelet/bubbletea v0.25.0
|
||||||
github.com/charmbracelet/lipgloss v0.9.1
|
github.com/charmbracelet/lipgloss v0.9.1
|
||||||
github.com/dave/jennifer v1.7.0
|
github.com/dave/jennifer v1.7.0
|
||||||
@@ -42,6 +44,7 @@ require (
|
|||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/iancoleman/strcase v0.3.0
|
github.com/iancoleman/strcase v0.3.0
|
||||||
github.com/invopop/jsonschema v0.7.0
|
github.com/invopop/jsonschema v0.7.0
|
||||||
|
github.com/jedib0t/go-pretty/v6 v6.5.3
|
||||||
github.com/jinzhu/copier v0.4.0
|
github.com/jinzhu/copier v0.4.0
|
||||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||||
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
|
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
|
||||||
@@ -76,12 +79,6 @@ require (
|
|||||||
modernc.org/sqlite v1.28.0
|
modernc.org/sqlite v1.28.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
|
||||||
github.com/charmbracelet/bubbles v0.17.1
|
|
||||||
github.com/jedib0t/go-pretty/v6 v6.5.3
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||||
@@ -101,7 +98,7 @@ require (
|
|||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.3 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
github.com/containerd/cgroups v1.1.0 // indirect
|
github.com/containerd/cgroups v1.1.0 // indirect
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||||
github.com/containerd/containerd v1.7.11 // indirect
|
github.com/containerd/containerd v1.7.11 // indirect
|
||||||
|
|||||||
@@ -163,8 +163,9 @@ github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2u
|
|||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Config struct {
|
|||||||
|
|
||||||
func DefaultConfig() Config {
|
func DefaultConfig() Config {
|
||||||
return Config{
|
return Config{
|
||||||
|
Binary: binary.DefaultCatalogerConfig(),
|
||||||
Golang: golang.DefaultCatalogerConfig(),
|
Golang: golang.DefaultCatalogerConfig(),
|
||||||
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
||||||
Python: python.DefaultCatalogerConfig(),
|
Python: python.DefaultCatalogerConfig(),
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ Package binary provides a concrete Cataloger implementations for surfacing possi
|
|||||||
package binary
|
package binary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
@@ -28,6 +30,15 @@ func NewCataloger(cfg CatalogerConfig) pkg.Cataloger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg CatalogerConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
// only keep the class names
|
||||||
|
var names []string
|
||||||
|
for _, cls := range cfg.Classifiers {
|
||||||
|
names = append(names, cls.Class)
|
||||||
|
}
|
||||||
|
return json.Marshal(names)
|
||||||
|
}
|
||||||
|
|
||||||
// Cataloger is the cataloger responsible for surfacing evidence of a very limited set of binary files,
|
// Cataloger is the cataloger responsible for surfacing evidence of a very limited set of binary files,
|
||||||
// which have been identified by the classifiers. The Cataloger is _NOT_ a place to catalog any and every
|
// which have been identified by the classifiers. The Cataloger is _NOT_ a place to catalog any and every
|
||||||
// binary, but rather the specific set that has been curated to be important, predominantly related to toolchain-
|
// binary, but rather the specific set that has been curated to be important, predominantly related to toolchain-
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/anchore/packageurl-go"
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
|
"github.com/anchore/syft/syft/cpe"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/testutil"
|
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/testutil"
|
||||||
@@ -871,7 +873,7 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
logicalFixture: "wp-cli/2.9.0/linux-amd64",
|
logicalFixture: "wp/2.9.0/linux-amd64",
|
||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "wp-cli",
|
Name: "wp-cli",
|
||||||
Version: "2.9.0",
|
Version: "2.9.0",
|
||||||
@@ -1253,3 +1255,49 @@ func Test_Cataloger_ResilientToErrors(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, resolver.searchCalled)
|
assert.True(t, resolver.searchCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCatalogerConfig_MarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cfg CatalogerConfig
|
||||||
|
want string
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "only show names of classes",
|
||||||
|
cfg: CatalogerConfig{
|
||||||
|
Classifiers: []Classifier{
|
||||||
|
{
|
||||||
|
Class: "class",
|
||||||
|
FileGlob: "glob",
|
||||||
|
EvidenceMatcher: FileContentsVersionMatcher(".thing"),
|
||||||
|
Package: "pkg",
|
||||||
|
PURL: packageurl.PackageURL{
|
||||||
|
Type: "type",
|
||||||
|
Namespace: "namespace",
|
||||||
|
Name: "name",
|
||||||
|
Version: "version",
|
||||||
|
Qualifiers: nil,
|
||||||
|
Subpath: "subpath",
|
||||||
|
},
|
||||||
|
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `["class"]`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.wantErr == nil {
|
||||||
|
tt.wantErr = assert.NoError
|
||||||
|
}
|
||||||
|
got, err := tt.cfg.MarshalJSON()
|
||||||
|
if !tt.wantErr(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.want, string(got))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
"debug/pe"
|
"debug/pe"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -44,6 +45,31 @@ type Classifier struct {
|
|||||||
CPEs []cpe.CPE `json:"cpes"`
|
CPEs []cpe.CPE `json:"cpes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg Classifier) MarshalJSON() ([]byte, error) {
|
||||||
|
type marshalled struct {
|
||||||
|
Class string `json:"class"`
|
||||||
|
FileGlob string `json:"fileGlob"`
|
||||||
|
Package string `json:"package"`
|
||||||
|
PURL string `json:"purl"`
|
||||||
|
CPEs []string `json:"cpes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var marshalledCPEs []string
|
||||||
|
for _, c := range cfg.CPEs {
|
||||||
|
marshalledCPEs = append(marshalledCPEs, c.BindToFmtString())
|
||||||
|
}
|
||||||
|
|
||||||
|
m := marshalled{
|
||||||
|
Class: cfg.Class,
|
||||||
|
FileGlob: cfg.FileGlob,
|
||||||
|
Package: cfg.Package,
|
||||||
|
PURL: cfg.PURL.String(),
|
||||||
|
CPEs: marshalledCPEs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(m)
|
||||||
|
}
|
||||||
|
|
||||||
// EvidenceMatcher is a function called to catalog Packages that match some sort of evidence
|
// EvidenceMatcher is a function called to catalog Packages that match some sort of evidence
|
||||||
type EvidenceMatcher func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error)
|
type EvidenceMatcher func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error)
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package binary
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/anchore/packageurl-go"
|
||||||
"github.com/anchore/syft/syft/cpe"
|
"github.com/anchore/syft/syft/cpe"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
)
|
)
|
||||||
@@ -83,3 +85,46 @@ func Test_ClassifierCPEs(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClassifier_MarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
classifier Classifier
|
||||||
|
want string
|
||||||
|
wantErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "go case",
|
||||||
|
classifier: Classifier{
|
||||||
|
Class: "class",
|
||||||
|
FileGlob: "glob",
|
||||||
|
EvidenceMatcher: FileContentsVersionMatcher(".thing"),
|
||||||
|
Package: "pkg",
|
||||||
|
PURL: packageurl.PackageURL{
|
||||||
|
Type: "type",
|
||||||
|
Namespace: "namespace",
|
||||||
|
Name: "name",
|
||||||
|
Version: "version",
|
||||||
|
Qualifiers: nil,
|
||||||
|
Subpath: "subpath",
|
||||||
|
},
|
||||||
|
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*")},
|
||||||
|
},
|
||||||
|
want: `{"class":"class","fileGlob":"glob","package":"pkg","purl":"pkg:type/namespace/name@version#subpath","cpes":["cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"]}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.wantErr == nil {
|
||||||
|
tt.wantErr = assert.NoError
|
||||||
|
}
|
||||||
|
cfg := tt.classifier
|
||||||
|
got, err := cfg.MarshalJSON()
|
||||||
|
if !tt.wantErr(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.want, string(got))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
|||||||
|
name: wp
|
||||||
|
offset: 6967657
|
||||||
|
length: 120
|
||||||
|
snippetSha256: fe7afa7db1b4efc55b3099b5b1fb46a34e34b147c708be0a74d1dd9fce2b5cac
|
||||||
|
fileSha256: af6b7ccc21ed0907cb504db5a059f0e120117905a6017bfdd4375cee3c93d864
|
||||||
|
|
||||||
|
### byte snippet to follow ###
|
||||||
|
ND CERTIFICATE-----
|
||||||
|
wp-cli/wp-cli 2.9.0 2023-10-25T09:10:40+00:00
|
||||||
|
composer/ca-bundle 1.3.7 76e46335014860eec1aa5a724799a
|
||||||
@@ -315,6 +315,13 @@ from-images:
|
|||||||
paths:
|
paths:
|
||||||
- /usr/local/bin/redis-server
|
- /usr/local/bin/redis-server
|
||||||
|
|
||||||
|
- version: 2.9.0
|
||||||
|
images:
|
||||||
|
- ref: wordpress:cli-2.9.0-php8.3@sha256:3a008aafccc6a8d2d7ec2b069722cb43b3569b61cd833f9f2f13d74a9284746e
|
||||||
|
platform: linux/amd64
|
||||||
|
paths:
|
||||||
|
- /usr/local/bin/wp
|
||||||
|
|
||||||
- version: 1.9.3p551
|
- version: 1.9.3p551
|
||||||
images:
|
images:
|
||||||
- ref: ruby:1.9.3-p551@sha256:56b4a74e4fc2492b3b857bc94454dfa910f61e823a4bfab275d279bfa218eb05
|
- ref: ruby:1.9.3-p551@sha256:56b4a74e4fc2492b3b857bc94454dfa910f61e823a4bfab275d279bfa218eb05
|
||||||
|
|||||||
Reference in New Issue
Block a user