Compare commits

..
Author SHA1 Message Date
Laurent Goderre 873f6e4037 Added classifier for wordpress cli binary
Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
2024-01-15 17:40:26 -05:00
391 changed files with 1336 additions and 5387 deletions
+1 -1
View File
@@ -90,7 +90,7 @@ tools:
- name: gh
version:
want: v2.42.1
want: v2.42.0
method: github-release
with:
repo: cli/cli
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
uses: ./.github/actions/bootstrap
- name: Restore base benchmark result
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: test/results/benchmark-main.txt
# use base sha for PR or new commit hash for main push in benchmark result key
@@ -39,7 +39,7 @@ jobs:
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
- uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4.1.0
with:
name: benchmark-test-results
path: test/results/**/*
+3 -3
View File
@@ -45,7 +45,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@0b21cf2492b6b02c465a3e5d7c473717ad7721ba #v3.23.1
uses: github/codeql-action/init@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 #v3.23.0
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@0b21cf2492b6b02c465a3e5d7c473717ad7721ba #v3.23.1
uses: github/codeql-action/autobuild@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 #v3.23.0
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@0b21cf2492b6b02c465a3e5d7c473717ad7721ba #v3.23.1
uses: github/codeql-action/analyze@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 #v3.23.0
@@ -1,10 +0,0 @@
name: Dependabot Automation
on:
pull_request:
permissions:
pull-requests: write
jobs:
run:
uses: anchore/workflows/.github/workflows/dependabot-automation.yaml@main
+1 -1
View File
@@ -141,7 +141,7 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
- uses: anchore/sbom-action@24b0d5238516480139aa8bc6f92eeb7b54a9eb0a #v0.15.5
- uses: anchore/sbom-action@c7f031d9249a826a082ea14c79d3b686a51d485a #v0.15.3
continue-on-error: true
with:
artifact-name: sbom.spdx.json
+15 -15
View File
@@ -37,31 +37,31 @@ jobs:
uses: ./.github/actions/bootstrap
- name: Restore Java test-fixture cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
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' ) }}
- name: Restore RPM test-fixture cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: syft/pkg/cataloger/redhat/test-fixtures/rpms
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/redhat/test-fixtures/rpms.fingerprint' ) }}
- name: Restore go binary test-fixture cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
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' ) }}
- name: Restore binary cataloger test-fixture cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
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' ) }}
- name: Restore Kernel test-fixture cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: syft/pkg/cataloger/kernel/test-fixtures/cache
key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }}
@@ -84,10 +84,10 @@ jobs:
run: make validate-cyclonedx-schema
- name: Restore integration test cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: ${{ github.workspace }}/cmd/syft/internal/test/integration/test-fixtures/cache
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('/cmd/syft/internal/test/integration/test-fixtures/cache.fingerprint') }}
path: ${{ github.workspace }}/test/integration/test-fixtures/cache
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }}
- name: Run integration tests
run: make integration
@@ -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).
# see https://github.com/actions/upload-artifact/issues/199 for more info
- name: Upload snapshot artifacts
uses: actions/cache/save@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache/save@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
# 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
@@ -134,7 +134,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache/restore@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: |
snapshot
@@ -152,7 +152,7 @@ jobs:
- name: Restore install.sh test image cache
id: install-test-image-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: ${{ github.workspace }}/test/install/cache
key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }}
@@ -187,7 +187,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache/restore@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: |
snapshot
@@ -202,7 +202,7 @@ jobs:
- name: Restore docker image cache for compare testing
id: mac-compare-testing-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: image.tar
key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }}
@@ -226,14 +226,14 @@ jobs:
uses: ./.github/actions/bootstrap
- name: Restore CLI test-fixture cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: ${{ github.workspace }}/test/cli/test-fixtures/cache
key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }}
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
uses: actions/cache/restore@e12d46a63a90f2fae62d114769bbf2a179198b5c #v3.3.3
with:
path: |
snapshot
+1
View File
@@ -32,6 +32,7 @@ VERSION
/test/results
coverage.txt
*.log
test/integration/test-fixtures/**/go.sum
# probable archives
.images
+1 -1
View File
@@ -234,7 +234,7 @@ rough outline how that works:
the `/test` directory. These tests should focus on correctness of functionality in depth. % test coverage metrics
only considers unit tests and no other forms of testing.
- `integration`: located within `cmd/syft/internal/test/integration`, these tests focus on the behavior surfaced by the common library
- `integration`: located within `test/integration`, these tests focus on the behavior surfaced by the common library
entrypoints from the `syft` package and make light assertions about the results surfaced. Additionally, these tests
tend to make diversity assertions for enum-like objects, ensuring that as enum values are added to a definition
that integration tests will automatically fail if no test attempts to use that enum value. For more details see
+62 -55
View File
@@ -502,20 +502,19 @@ Configuration options (example values are the default):
```yaml
# the output format(s) of the SBOM report (options: syft-table, syft-text, syft-json, spdx-json, ...)
# same as -o, --output, and SYFT_OUTPUT env var
# to specify multiple output files in differing formats, use a list:
# output:
# - "syft-json=<syft-json-output-file>"
# - "spdx-json=<spdx-json-output-file>"
# SYFT_OUTPUT env var / -o, --output flags
output:
- "syft-table"
output: "syft-table"
# suppress all output (except for the SBOM report)
# SYFT_QUIET env var / -q flag
# same as -q ; SYFT_QUIET env var
quiet: false
# enable/disable checking for application updates on startup
# SYFT_CHECK_FOR_APP_UPDATE env var
# same as SYFT_CHECK_FOR_APP_UPDATE env var
check-for-app-update: true
# maximum number of workers used to process the list of package catalogers in parallel
@@ -525,11 +524,15 @@ parallelism: 1
# exclude:
# - "/etc/**"
# - "./out/**/*.json"
# SYFT_EXCLUDE env var / --exclude flag
# same as --exclude ; SYFT_EXCLUDE env var
exclude: []
# allows users to exclude synthetic binary packages from the sbom
# these packages are removed if an overlap with a non-synthetic package is found
exclude-binary-overlap-by-ownership: true
# os and/or architecture to use when referencing container images (e.g. "windows/armv6" or "arm64")
# SYFT_PLATFORM env var / --platform flag
# same as --platform; SYFT_PLATFORM env var
platform: ""
# the search space to look for file and package data (options: all-layers, squashed)
@@ -538,24 +541,48 @@ scope: "squashed"
# set the list of package catalogers to use when generating the SBOM
# default = empty (cataloger set determined automatically by the source type [image or file/directory])
# Use `syft cataloger list` for a list of catalogers you can specify
# DEPRECATED: please use default-catalogers and select-catalogers configuration options instead
# SYFT_CATALOGERS env var / --catalogers flag
# catalogers:
# - alpm-db-cataloger
# - apkdb-cataloger
# - binary-cataloger
# - cargo-auditable-binary-cataloger
# - cocoapods-cataloger
# - conan-cataloger
# - dartlang-lock-cataloger
# - dotnet-deps-cataloger
# - dotnet-portable-executable-cataloger
# - dpkg-db-cataloger
# - elixir-mix-lock-cataloger
# - erlang-rebar-lock-cataloger
# - go-module-file-cataloger
# - go-module-binary-cataloger
# - graalvm-native-image-cataloger
# - haskell-cataloger
# - java-cataloger
# - java-gradle-lockfile-cataloger
# - java-pom-cataloger
# - javascript-lock-cataloger
# - javascript-package-cataloger
# - linux-kernel-cataloger
# - nix-store-cataloger
# - php-composer-installed-cataloger
# - php-composer-lock-cataloger
# - portage-cataloger
# - python-package-cataloger
# - python-installed-package-cataloger
# - rpm-db-cataloger
# - rpm-archive-cataloger
# - ruby-gemfile-cataloger
# - ruby-installed-gemspec-cataloger
# - rust-cargo-lock-cataloger
# - sbom-cataloger
# - spm-cataloger
catalogers:
# set the base set of catalogers to use (defaults to 'image' or 'directory' depending on the scan source)
# SYFT_DEFAULT_CATALOGERS env var / --override-default-catalogers flag
default-catalogers: []
# add, remove, and filter the catalogers to be used
# SYFT_SELECT_CATALOGERS env var / --select-cataloger flag;
select-catalogers: []
# all format configuration
format:
# default value for all formats that support the "pretty" option (default is unset)
# SYFT_FORMAT_PRETTY env var
pretty:
# all syft-json format options
@@ -563,7 +590,6 @@ format:
# include space indention and newlines (inherits default value from 'format.pretty' or 'false' if parent is unset)
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
# SYFT_FORMAT_JSON_PRETTY env var
pretty: false
# transform any syft-json output to conform to an approximation of the v11.0.1 schema. This includes:
@@ -573,30 +599,21 @@ format:
# that output might not strictly be json schema v11 compliant, however, for consumers that require time to port
# over to the final syft 1.0 json output this option can be used to ease the transition.
#
# Note: long term support for this option is not guaranteed (it may change or break at any time).
# SYFT_FORMAT_JSON_LEGACY env var
# Note: long term support for this option is not guaranteed.
legacy: false
# all template format options
template:
# path to the template file to use when rendering the output with the `template` output format.
# Note that all template paths are based on the current syft-json schema.
# SYFT_FORMAT_TEMPLATE_PATH env var / -t flag
# same as -t ; SYFT_TEMPLATE_PATH env var
path: ""
# if true, uses the go structs for the syft-json format for templating.
# if false, uses the syft-json output for templating (which follows the syft JSON schema exactly).
#
# Note: long term support for this option is not guaranteed (it may change or break at any time).
# SYFT_FORMAT_TEMPLATE_LEGACY env var
legacy: false
# all spdx-json format options
spdx-json:
# include space indention and newlines
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
# SYFT_FORMAT_SPDX_JSON_PRETTY env var
pretty: false
# all cyclonedx-json format options
@@ -604,7 +621,6 @@ format:
# include space indention and newlines
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
# SYFT_FORMAT_CYCLONEDX_JSON_PRETTY env var
pretty: false
# all cyclonedx-xml format options
@@ -612,7 +628,6 @@ format:
# include space indention
# note: inherits default value from 'format.pretty' or 'false' if parent is unset
# SYFT_FORMAT_CYCLONEDX_XML_PRETTY env var
pretty: false
@@ -621,17 +636,16 @@ file:
metadata:
# select which files should be captured by the file-metadata cataloger and included in the SBOM.
# Options include:
# - "all": capture all files from the search space
# - "owned-by-package": capture only files owned by packages
# - "none", "": do not capture any files
# - "all-files": capture all files from the search space
# - "owned-files": capture only files owned by packages
# - "unowned-files": capture only files not owned by packages
# - "no-files", "": do not capture any files
# SYFT_FILE_METADATA_SELECTION env var
selection: "owned-by-package"
selection: "owned-files"
# the file digest algorithms to use when cataloging files (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")
# SYFT_FILE_METADATA_DIGESTS env var
digests:
- "sha256"
- "sha1"
digests: ["sha256"]
# capture the contents of select files in the SBOM
content:
@@ -660,7 +674,6 @@ package:
# allows users to exclude synthetic binary packages from the sbom
# these packages are removed if an overlap with a non-synthetic package is found
# SYFT_PACKAGE_EXCLUDE_BINARY_OVERLAP_BY_OWNERSHIP env var
exclude-binary-overlap-by-ownership: true
@@ -719,23 +732,17 @@ javascript:
# configuration for the source that the SBOM is generated from (e.g. a file, directory, or container image)
source:
# alias name for the source
# SYFT_SOURCE_NAME env var / --source-name flag
# SYFT_SOURCE_NAME env var; --source-name flag
name: ""
# alias version for the source
# SYFT_SOURCE_VERSION env var / --source-version flag
# SYFT_SOURCE_VERSION env var; --source-version flag
version: ""
# base directory for scanning, no links will be followed above this directory, and all paths will be
# reported relative to this directory
# SYFT_SOURCE_BASE_PATH env var
base-path: ''
# options affecting the file source type
# options affecting the file source type
file:
# the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")
digests:
- "sha256"
digests: ["sha256"]
image:
@@ -797,15 +804,15 @@ attest:
log:
# use structured logging
# SYFT_LOG_STRUCTURED env var
# same as SYFT_LOG_STRUCTURED env var
structured: false
# the log level; note: detailed logging suppress the ETUI
# SYFT_LOG_LEVEL env var
# same as SYFT_LOG_LEVEL env var
level: "error"
# location to write the log file (default is not to have a log file)
# SYFT_LOG_FILE env var
# same as SYFT_LOG_FILE env var
file: ""
```
+5 -5
View File
@@ -185,7 +185,7 @@ tasks:
- fixtures
vars:
TEST_PKGS:
sh: "go list ./... | grep -v {{ .OWNER }}/{{ .PROJECT }}/test | grep -v {{ .OWNER }}/{{ .PROJECT }}/cmd/syft/internal/test | tr '\n' ' '"
sh: "go list ./... | grep -v {{ .OWNER }}/{{ .PROJECT }}/test | tr '\n' ' '"
# unit test coverage threshold (in % coverage)
COVERAGE_THRESHOLD: 62
@@ -197,7 +197,7 @@ tasks:
integration:
desc: Run integration tests
cmds:
- "go test -v ./cmd/syft/internal/test/integration"
- "go test -v ./test/integration"
# exercise most of the CLI with the data race detector
- "go run -race cmd/syft/main.go alpine:latest"
@@ -265,7 +265,7 @@ tasks:
fingerprints:
desc: Generate test fixture fingerprints
generates:
- cmd/syft/internal/test/integration/test-fixtures/cache.fingerprint
- test/integration/test-fixtures/cache.fingerprint
- syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint
- syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint
- syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint
@@ -275,7 +275,7 @@ tasks:
- test/cli/test-fixtures/cache.fingerprint
cmds:
# for IMAGE integration test fixtures
- "cd cmd/syft/internal/test/integration/test-fixtures && make cache.fingerprint"
- "cd test/integration/test-fixtures && make cache.fingerprint"
# for BINARY test fixtures
- "cd syft/pkg/cataloger/binary/test-fixtures && make cache.fingerprint"
# for JAVA BUILD test fixtures
@@ -286,7 +286,7 @@ tasks:
- "cd syft/pkg/cataloger/redhat/test-fixtures && make rpms.fingerprint"
# for Kernel test fixtures
- "cd syft/pkg/cataloger/kernel/test-fixtures && make cache.fingerprint"
# for INSTALL test fixtures
# for INSTALL integration test fixtures
- "cd test/install && make cache.fingerprint"
# for CLI test fixtures
- "cd test/cli/test-fixtures && make cache.fingerprint"
+1 -2
View File
@@ -9,8 +9,8 @@ import (
"github.com/anchore/clio"
"github.com/anchore/stereoscope"
"github.com/anchore/syft/cmd/syft/cli/commands"
handler "github.com/anchore/syft/cmd/syft/cli/ui"
"github.com/anchore/syft/cmd/syft/internal/commands"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
@@ -86,7 +86,6 @@ func create(id clio.Identification, out io.Writer) (clio.Application, *cobra.Com
rootCmd.AddCommand(
scanCmd,
commands.Packages(app, scanCmd), // this is currently an alias for the scan command
commands.Cataloger(app),
commands.Attest(app),
commands.Convert(app),
clio.VersionCommand(id),
@@ -13,7 +13,7 @@ import (
"github.com/wagoodman/go-progress"
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/bus"
@@ -30,7 +30,7 @@ import (
)
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
attestHelp = attestExample + attestSchemeHelp
@@ -232,7 +232,7 @@ func attestCommand(sbomFilepath string, opts *attestOptions, userInput string) (
}
func predicateType(outputName string) string {
// select the Cosign predicate type based on defined output type
// Select Cosign predicate type based on defined output type
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
switch strings.ToLower(outputName) {
case "cyclonedx-json":
@@ -14,7 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
@@ -8,7 +8,7 @@ import (
"github.com/spf13/cobra"
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
@@ -5,7 +5,6 @@ import (
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal"
)
func Packages(app clio.Application, scanCmd *cobra.Command) *cobra.Command {
@@ -14,14 +13,11 @@ func Packages(app clio.Application, scanCmd *cobra.Command) *cobra.Command {
opts := defaultScanOptions()
cmd := app.SetupCommand(&cobra.Command{
Use: "packages [SOURCE]",
Short: scanCmd.Short,
Long: scanCmd.Long,
Args: scanCmd.Args,
Example: internal.Tprintf(scanHelp, map[string]interface{}{
"appName": id.Name,
"command": "packages",
}),
Use: "packages [SOURCE]",
Short: scanCmd.Short,
Long: scanCmd.Long,
Args: scanCmd.Args,
Example: scanCmd.Example,
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
RunE: func(cmd *cobra.Command, args []string) error {
restoreStdout := ui.CaptureStdoutToTraceLog()
@@ -14,7 +14,7 @@ import (
"github.com/anchore/clio"
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/bus"
@@ -132,7 +132,7 @@ func (o *scanOptions) validateLegacyOptionsNotUsed() error {
}
if legacy.ExcludeBinaryOverlapByOwnership != nil {
return fmt.Errorf("the config file option 'exclude-binary-overlap-by-ownership' has been removed, please use 'package.exclude-binary-overlap-by-ownership' instead")
return fmt.Errorf("the config file option 'exclude-binary-overlap-by-ownership' has been removed, please use 'relationships.exclude-binary-packages-with-file-ownership-overlap' instead")
}
if legacy.BasePath != nil {
@@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/cmd/syft/cli/options"
)
func Test_scanOptions_validateLegacyOptionsNotUsed(t *testing.T) {
@@ -29,7 +29,7 @@ func Test_scanOptions_validateLegacyOptionsNotUsed(t *testing.T) {
{
name: "config file with exclude-binary-overlap-by-ownership legacy option",
cfg: "test-fixtures/scan-configs/with-exclude-binary-overlap-by-ownership.yaml",
wantErr: assertErrorContains("package.exclude-binary-overlap-by-ownership"),
wantErr: assertErrorContains("relationships.exclude-binary-packages-with-file-ownership-overlap"),
},
{
name: "config file with file string legacy option",
@@ -11,8 +11,8 @@ import (
"github.com/anchore/clio"
hashiVersion "github.com/anchore/go-version"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/cmd/syft/internal"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event"
@@ -46,6 +46,7 @@ type Catalog struct {
// configuration for the source (the subject being analyzed)
Registry registryConfig `yaml:"registry" json:"registry" mapstructure:"registry"`
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
Name string `yaml:"name" json:"name" mapstructure:"name"` // deprecated
Source sourceConfig `yaml:"source" json:"source" mapstructure:"source"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
}
@@ -174,6 +175,14 @@ func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
flags.StringArrayVarP(&cfg.SelectCatalogers, "select-catalogers", "",
"add, remove, and filter the catalogers to be used")
flags.StringVarP(&cfg.Source.Name, "name", "",
"set the name of the target being analyzed")
if pfp, ok := flags.(fangs.PFlagSetProvider); ok {
flagSet := pfp.PFlagSet()
flagSet.Lookup("name").Deprecated = "use: source-name"
}
flags.StringVarP(&cfg.Source.Name, "source-name", "",
"set the name of the target being analyzed")
@@ -189,6 +198,13 @@ func (cfg *Catalog) DescribeFields(descriptions fangs.FieldDescriptionSet) {
}
func (cfg *Catalog) PostLoad() error {
if cfg.Name != "" {
log.Warnf("name parameter is deprecated. please use: source-name. name will be removed in a future version")
if cfg.Source.Name == "" {
cfg.Source.Name = cfg.Name
}
}
usingLegacyCatalogers := len(cfg.Catalogers) > 0
usingNewCatalogers := len(cfg.DefaultCatalogers) > 0 || len(cfg.SelectCatalogers) > 0
@@ -2,9 +2,6 @@ package options
import (
"fmt"
"sort"
"github.com/scylladb/go-set/strset"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/syft/file"
@@ -38,10 +35,6 @@ func defaultFileConfig() fileConfig {
}
func (c *fileConfig) PostLoad() error {
digests := strset.New(c.Metadata.Digests...).List()
sort.Strings(digests)
c.Metadata.Digests = digests
switch c.Metadata.Selection {
case file.NoFilesSelection, file.FilesOwnedByPackageSelection, file.AllFilesSelection:
return nil
+129
View File
@@ -0,0 +1,129 @@
package options
import (
"fmt"
"github.com/hashicorp/go-multierror"
"github.com/anchore/clio"
"github.com/anchore/syft/syft/format/cyclonedxjson"
"github.com/anchore/syft/syft/format/cyclonedxxml"
"github.com/anchore/syft/syft/format/github"
"github.com/anchore/syft/syft/format/spdxjson"
"github.com/anchore/syft/syft/format/spdxtagvalue"
"github.com/anchore/syft/syft/format/syftjson"
"github.com/anchore/syft/syft/format/table"
"github.com/anchore/syft/syft/format/template"
"github.com/anchore/syft/syft/format/text"
"github.com/anchore/syft/syft/sbom"
)
var _ clio.PostLoader = (*Format)(nil)
// Format contains all user configuration for output formatting.
type Format struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
Template FormatTemplate `yaml:"template" json:"template" mapstructure:"template"`
SyftJSON FormatSyftJSON `yaml:"json" json:"json" mapstructure:"json"`
SPDXJSON FormatSPDXJSON `yaml:"spdx-json" json:"spdx-json" mapstructure:"spdx-json"`
CyclonedxJSON FormatCyclonedxJSON `yaml:"cyclonedx-json" json:"cyclonedx-json" mapstructure:"cyclonedx-json"`
CyclonedxXML FormatCyclonedxXML `yaml:"cyclonedx-xml" json:"cyclonedx-xml" mapstructure:"cyclonedx-xml"`
}
func (o *Format) PostLoad() error {
o.SyftJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SyftJSON.Pretty)
o.SPDXJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SPDXJSON.Pretty)
o.CyclonedxJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxJSON.Pretty)
o.CyclonedxXML.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxXML.Pretty)
return nil
}
func DefaultFormat() Format {
return Format{
Template: DefaultFormatTemplate(),
SyftJSON: DefaultFormatJSON(),
SPDXJSON: DefaultFormatSPDXJSON(),
CyclonedxJSON: DefaultFormatCyclonedxJSON(),
CyclonedxXML: DefaultFormatCyclonedxXML(),
}
}
func (o *Format) Encoders() ([]sbom.FormatEncoder, error) {
// setup all encoders based on the configuration
var list encoderList
// in the future there will be application configuration options that can be used to set the default output format
list.addWithErr(template.ID)(o.Template.formatEncoders())
list.addWithErr(syftjson.ID)(o.SyftJSON.formatEncoders())
list.add(table.ID)(table.NewFormatEncoder())
list.add(text.ID)(text.NewFormatEncoder())
list.add(github.ID)(github.NewFormatEncoder())
list.addWithErr(cyclonedxxml.ID)(o.CyclonedxXML.formatEncoders())
list.addWithErr(cyclonedxjson.ID)(o.CyclonedxJSON.formatEncoders())
list.addWithErr(spdxjson.ID)(o.SPDXJSON.formatEncoders())
list.addWithErr(spdxtagvalue.ID)(spdxTagValueEncoders())
return list.encoders, list.err
}
// TODO: when application configuration is made for this format then this should be ported to the options object
// that is created for that configuration (as done with the template output option)
func spdxTagValueEncoders() ([]sbom.FormatEncoder, error) {
var (
encs []sbom.FormatEncoder
errs error
)
for _, v := range spdxtagvalue.SupportedVersions() {
enc, err := spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.EncoderConfig{Version: v})
if err != nil {
errs = multierror.Append(errs, err)
} else {
encs = append(encs, enc)
}
}
return encs, errs
}
type encoderList struct {
encoders []sbom.FormatEncoder
err error
}
func (l *encoderList) addWithErr(name sbom.FormatID) func([]sbom.FormatEncoder, error) {
return func(encs []sbom.FormatEncoder, err error) {
if err != nil {
l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: %w", name, err))
return
}
for _, enc := range encs {
if enc == nil {
l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: nil encoder returned", name))
continue
}
l.encoders = append(l.encoders, enc)
}
}
}
func (l *encoderList) add(name sbom.FormatID) func(...sbom.FormatEncoder) {
return func(encs ...sbom.FormatEncoder) {
for _, enc := range encs {
if enc == nil {
l.err = multierror.Append(l.err, fmt.Errorf("unable to configure %q format encoder: nil encoder returned", name))
continue
}
l.encoders = append(l.encoders, enc)
}
}
}
func multiLevelOption[T any](defaultValue T, option ...*T) *T {
result := defaultValue
for _, opt := range option {
if opt != nil {
result = *opt
}
}
return &result
}
@@ -0,0 +1,43 @@
package options
import (
"github.com/hashicorp/go-multierror"
"github.com/anchore/syft/syft/format/cyclonedxjson"
"github.com/anchore/syft/syft/sbom"
)
type FormatCyclonedxJSON struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
}
func DefaultFormatCyclonedxJSON() FormatCyclonedxJSON {
return FormatCyclonedxJSON{}
}
func (o FormatCyclonedxJSON) formatEncoders() ([]sbom.FormatEncoder, error) {
var (
encs []sbom.FormatEncoder
errs error
)
for _, v := range cyclonedxjson.SupportedVersions() {
enc, err := cyclonedxjson.NewFormatEncoderWithConfig(o.buildConfig(v))
if err != nil {
errs = multierror.Append(errs, err)
} else {
encs = append(encs, enc)
}
}
return encs, errs
}
func (o FormatCyclonedxJSON) buildConfig(version string) cyclonedxjson.EncoderConfig {
var pretty bool
if o.Pretty != nil {
pretty = *o.Pretty
}
return cyclonedxjson.EncoderConfig{
Version: version,
Pretty: pretty,
}
}
@@ -13,7 +13,7 @@ func TestFormatCyclonedxJSON_buildConfig(t *testing.T) {
ft := &FormatCyclonedxJSON{}
ft = setAllToNonZero(t, ft).(*FormatCyclonedxJSON)
subject := ft.config("Version")
subject := ft.buildConfig("Version")
assertExpectedValue(t, subject)
}
@@ -0,0 +1,43 @@
package options
import (
"github.com/hashicorp/go-multierror"
"github.com/anchore/syft/syft/format/cyclonedxxml"
"github.com/anchore/syft/syft/sbom"
)
type FormatCyclonedxXML struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
}
func DefaultFormatCyclonedxXML() FormatCyclonedxXML {
return FormatCyclonedxXML{}
}
func (o FormatCyclonedxXML) formatEncoders() ([]sbom.FormatEncoder, error) {
var (
encs []sbom.FormatEncoder
errs error
)
for _, v := range cyclonedxxml.SupportedVersions() {
enc, err := cyclonedxxml.NewFormatEncoderWithConfig(o.buildConfig(v))
if err != nil {
errs = multierror.Append(errs, err)
} else {
encs = append(encs, enc)
}
}
return encs, errs
}
func (o FormatCyclonedxXML) buildConfig(version string) cyclonedxxml.EncoderConfig {
var pretty bool
if o.Pretty != nil {
pretty = *o.Pretty
}
return cyclonedxxml.EncoderConfig{
Version: version,
Pretty: pretty,
}
}
@@ -10,6 +10,6 @@ func TestFormatCyclonedxXML_buildConfig(t *testing.T) {
ft := FormatCyclonedxXML{}
ftp := setAllToNonZero(t, &ft).(*FormatCyclonedxXML)
subject := ftp.config("Version")
subject := ftp.buildConfig("Version")
assertExpectedValue(t, subject)
}
+43
View File
@@ -0,0 +1,43 @@
package options
import (
"github.com/hashicorp/go-multierror"
"github.com/anchore/syft/syft/format/spdxjson"
"github.com/anchore/syft/syft/sbom"
)
type FormatSPDXJSON struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
}
func DefaultFormatSPDXJSON() FormatSPDXJSON {
return FormatSPDXJSON{}
}
func (o FormatSPDXJSON) formatEncoders() ([]sbom.FormatEncoder, error) {
var (
encs []sbom.FormatEncoder
errs error
)
for _, v := range spdxjson.SupportedVersions() {
enc, err := spdxjson.NewFormatEncoderWithConfig(o.buildConfig(v))
if err != nil {
errs = multierror.Append(errs, err)
} else {
encs = append(encs, enc)
}
}
return encs, errs
}
func (o FormatSPDXJSON) buildConfig(v string) spdxjson.EncoderConfig {
var pretty bool
if o.Pretty != nil {
pretty = *o.Pretty
}
return spdxjson.EncoderConfig{
Version: v,
Pretty: pretty,
}
}
@@ -10,6 +10,6 @@ func TestFormatSPDXJSON_buildConfig(t *testing.T) {
ft := &FormatSPDXJSON{}
ft = setAllToNonZero(t, ft).(*FormatSPDXJSON)
subject := ft.config("Version")
subject := ft.buildConfig("Version")
assertExpectedValue(t, subject)
}
@@ -2,6 +2,7 @@ package options
import (
"github.com/anchore/syft/syft/format/syftjson"
"github.com/anchore/syft/syft/sbom"
)
type FormatSyftJSON struct {
@@ -15,7 +16,12 @@ func DefaultFormatJSON() FormatSyftJSON {
}
}
func (o FormatSyftJSON) config() syftjson.EncoderConfig {
func (o FormatSyftJSON) formatEncoders() ([]sbom.FormatEncoder, error) {
enc, err := syftjson.NewFormatEncoderWithConfig(o.buildConfig())
return []sbom.FormatEncoder{enc}, err
}
func (o FormatSyftJSON) buildConfig() syftjson.EncoderConfig {
var pretty bool
if o.Pretty != nil {
pretty = *o.Pretty
@@ -10,6 +10,6 @@ func TestFormatSyftJSON_buildConfig(t *testing.T) {
ft := &FormatSyftJSON{}
ft = setAllToNonZero(t, ft).(*FormatSyftJSON)
subject := ft.config()
subject := ft.buildConfig()
assertExpectedValue(t, subject)
}
@@ -3,6 +3,7 @@ package options
import (
"github.com/anchore/clio"
"github.com/anchore/syft/syft/format/template"
"github.com/anchore/syft/syft/sbom"
)
var _ clio.FlagAdder = (*FormatTemplate)(nil)
@@ -10,7 +11,6 @@ var _ clio.FlagAdder = (*FormatTemplate)(nil)
type FormatTemplate struct {
Enabled bool `yaml:"-" json:"-" mapstructure:"-"`
Path string `yaml:"path" json:"path" mapstructure:"path"` // -t template file to use for output
Legacy bool `yaml:"legacy" json:"legacy" mapstructure:"legacy"`
}
func DefaultFormatTemplate() FormatTemplate {
@@ -26,9 +26,12 @@ func (o *FormatTemplate) AddFlags(flags clio.FlagSet) {
}
}
func (o FormatTemplate) config() template.EncoderConfig {
return template.EncoderConfig{
TemplatePath: o.Path,
Legacy: o.Legacy,
func (o FormatTemplate) formatEncoders() ([]sbom.FormatEncoder, error) {
if !o.Enabled {
return nil, nil
}
enc, err := template.NewFormatEncoder(template.EncoderConfig{
TemplatePath: o.Path,
})
return []sbom.FormatEncoder{enc}, err
}
@@ -2,7 +2,6 @@ package options
import (
"fmt"
"sort"
"strings"
"github.com/scylladb/go-set/strset"
@@ -35,13 +34,6 @@ func defaultSourceConfig() sourceConfig {
}
}
func (c *fileSource) PostLoad() error {
digests := strset.New(c.Digests...).List()
sort.Strings(digests)
c.Digests = digests
return nil
}
func (c imageSource) PostLoad() error {
return checkDefaultSourceValues(c.DefaultPullSource)
}
-20
View File
@@ -1,20 +0,0 @@
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
}
@@ -1,267 +0,0 @@
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
}
@@ -1,201 +0,0 @@
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))
})
}
}
-56
View File
@@ -1,56 +0,0 @@
package options
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/file"
)
func Test_fileConfig_PostLoad(t *testing.T) {
tests := []struct {
name string
cfg fileConfig
assert func(t *testing.T, cfg fileConfig)
wantErr assert.ErrorAssertionFunc
}{
{
name: "deduplicate digests",
cfg: fileConfig{
Metadata: fileMetadata{
Selection: file.NoFilesSelection,
Digests: []string{"sha1", "sha1"},
},
},
assert: func(t *testing.T, cfg fileConfig) {
assert.Equal(t, []string{"sha1"}, cfg.Metadata.Digests)
},
},
{
name: "error on invalid selection",
cfg: fileConfig{
Metadata: fileMetadata{
Selection: file.Selection("invalid"),
},
},
wantErr: assert.Error,
},
{
name: "error on empty selection",
cfg: fileConfig{},
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = assert.NoError
}
tt.wantErr(t, tt.cfg.PostLoad())
if tt.assert != nil {
tt.assert(t, tt.cfg)
}
})
}
}
-58
View File
@@ -1,58 +0,0 @@
package options
import (
"github.com/anchore/clio"
"github.com/anchore/syft/syft/format"
"github.com/anchore/syft/syft/sbom"
)
var _ clio.PostLoader = (*Format)(nil)
// Format contains all user configuration for output formatting.
type Format struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
Template FormatTemplate `yaml:"template" json:"template" mapstructure:"template"`
SyftJSON FormatSyftJSON `yaml:"json" json:"json" mapstructure:"json"`
SPDXJSON FormatSPDXJSON `yaml:"spdx-json" json:"spdx-json" mapstructure:"spdx-json"`
CyclonedxJSON FormatCyclonedxJSON `yaml:"cyclonedx-json" json:"cyclonedx-json" mapstructure:"cyclonedx-json"`
CyclonedxXML FormatCyclonedxXML `yaml:"cyclonedx-xml" json:"cyclonedx-xml" mapstructure:"cyclonedx-xml"`
}
func (o *Format) PostLoad() error {
o.SyftJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SyftJSON.Pretty)
o.SPDXJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.SPDXJSON.Pretty)
o.CyclonedxJSON.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxJSON.Pretty)
o.CyclonedxXML.Pretty = multiLevelOption[bool](false, o.Pretty, o.CyclonedxXML.Pretty)
return nil
}
func DefaultFormat() Format {
return Format{
Template: DefaultFormatTemplate(),
SyftJSON: DefaultFormatJSON(),
SPDXJSON: DefaultFormatSPDXJSON(),
CyclonedxJSON: DefaultFormatCyclonedxJSON(),
CyclonedxXML: DefaultFormatCyclonedxXML(),
}
}
func (o Format) Encoders() ([]sbom.FormatEncoder, error) {
return format.EncodersConfig{
Template: o.Template.config(),
SyftJSON: o.SyftJSON.config(),
SPDXJSON: o.SPDXJSON.config(format.AllVersions), // we support multiple versions, not just a single version
CyclonedxJSON: o.CyclonedxJSON.config(format.AllVersions), // we support multiple versions, not just a single version
CyclonedxXML: o.CyclonedxXML.config(format.AllVersions), // we support multiple versions, not just a single version
}.Encoders()
}
func multiLevelOption[T any](defaultValue T, option ...*T) *T {
result := defaultValue
for _, opt := range option {
if opt != nil {
result = *opt
}
}
return &result
}
@@ -1,24 +0,0 @@
package options
import (
"github.com/anchore/syft/syft/format/cyclonedxjson"
)
type FormatCyclonedxJSON struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
}
func DefaultFormatCyclonedxJSON() FormatCyclonedxJSON {
return FormatCyclonedxJSON{}
}
func (o FormatCyclonedxJSON) config(version string) cyclonedxjson.EncoderConfig {
var pretty bool
if o.Pretty != nil {
pretty = *o.Pretty
}
return cyclonedxjson.EncoderConfig{
Version: version,
Pretty: pretty,
}
}
@@ -1,24 +0,0 @@
package options
import (
"github.com/anchore/syft/syft/format/cyclonedxxml"
)
type FormatCyclonedxXML struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
}
func DefaultFormatCyclonedxXML() FormatCyclonedxXML {
return FormatCyclonedxXML{}
}
func (o FormatCyclonedxXML) config(version string) cyclonedxxml.EncoderConfig {
var pretty bool
if o.Pretty != nil {
pretty = *o.Pretty
}
return cyclonedxxml.EncoderConfig{
Version: version,
Pretty: pretty,
}
}
@@ -1,24 +0,0 @@
package options
import (
"github.com/anchore/syft/syft/format/spdxjson"
)
type FormatSPDXJSON struct {
Pretty *bool `yaml:"pretty" json:"pretty" mapstructure:"pretty"`
}
func DefaultFormatSPDXJSON() FormatSPDXJSON {
return FormatSPDXJSON{}
}
func (o FormatSPDXJSON) config(v string) spdxjson.EncoderConfig {
var pretty bool
if o.Pretty != nil {
pretty = *o.Pretty
}
return spdxjson.EncoderConfig{
Version: v,
Pretty: pretty,
}
}
-37
View File
@@ -1,37 +0,0 @@
package options
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_fileSource_PostLoad(t *testing.T) {
tests := []struct {
name string
cfg fileSource
assert func(t *testing.T, cfg fileSource)
wantErr assert.ErrorAssertionFunc
}{
{
name: "deduplicate digests",
cfg: fileSource{
Digests: []string{"sha1", "sha1"},
},
assert: func(t *testing.T, cfg fileSource) {
assert.Equal(t, []string{"sha1"}, cfg.Digests)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = assert.NoError
}
tt.wantErr(t, tt.cfg.PostLoad())
if tt.assert != nil {
tt.assert(t, tt.cfg)
}
})
}
}
@@ -1,350 +0,0 @@
package integration
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"go/types"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/bmatcuk/doublestar/v4"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_packageCatalogerExports(t *testing.T) {
// sanity check that we are actually finding exports
exports := packageCatalogerExports(t)
require.NotEmpty(t, exports)
expectAtLeast := map[string]*strset.Set{
"golang": strset.New("NewGoModuleFileCataloger", "NewGoModuleBinaryCataloger", "CatalogerConfig", "DefaultCatalogerConfig"),
}
for pkg, expected := range expectAtLeast {
actual, ok := exports[pkg]
require.True(t, ok, pkg)
require.True(t, expected.IsSubset(actual.Names()), pkg)
}
}
func Test_validatePackageCatalogerExport(t *testing.T) {
cases := []struct {
name string
export exportToken
wantErr assert.ErrorAssertionFunc
}{
// valid...
{
name: "valid constructor",
export: exportToken{
Name: "NewFooCataloger",
Type: "*ast.FuncType",
SignatureSize: 1,
ReturnTypeNames: []string{
"pkg.Cataloger",
},
},
},
{
name: "valid default config",
export: exportToken{
Name: "DefaultFooConfig",
Type: "*ast.FuncType",
SignatureSize: 0,
},
},
{
name: "valid config",
export: exportToken{
Name: "FooConfig",
Type: "*ast.StructType",
},
},
// invalid...
{
name: "constructor that returns a concrete type",
export: exportToken{
Name: "NewFooCataloger",
Type: "*ast.FuncType",
SignatureSize: 1,
ReturnTypeNames: []string{
"*generic.Cataloger",
},
},
wantErr: assert.Error,
},
{
name: "struct with constructor name",
export: exportToken{
Name: "NewFooCataloger",
Type: "*ast.StructType",
},
wantErr: assert.Error,
},
{
name: "struct with default config fn name",
export: exportToken{
Name: "DefaultFooConfig",
Type: "*ast.StructType",
},
wantErr: assert.Error,
},
{
name: "fn with struct name",
export: exportToken{
Name: "FooConfig",
Type: "*ast.FuncType",
},
wantErr: assert.Error,
},
{
name: "default config with parameters",
export: exportToken{
Name: "DefaultFooConfig",
Type: "*ast.FuncType",
SignatureSize: 1,
},
wantErr: assert.Error,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if c.wantErr == nil {
c.wantErr = assert.NoError
}
err := validatePackageCatalogerExport(t, "test", c.export)
c.wantErr(t, err)
})
}
}
func Test_PackageCatalogerConventions(t *testing.T) {
// look at each package in syft/pkg/cataloger...
// we want to make certain that only the following things are exported from the package:
// - function matching New*Cataloger (e.g. NewAptCataloger)
// - function matching Default*Config
// - struct matching *Config
//
// anything else that is exported should result in the test failing.
// note: this is meant to apply to things in static space, not methods on structs or within interfaces.
//
// this additionally ensures that:
// - any config struct has a Default*Config function to pair with it.
// - all cataloger constructors return pkg.Cataloger interface instead of a concrete type
exportsPerPackage := packageCatalogerExports(t)
//for debugging purposes...
//for pkg, exports := range exportsPerPackage {
// t.Log(pkg)
// for _, export := range exports {
// t.Logf(" %#v", export)
// }
//}
for pkg, exports := range exportsPerPackage {
for _, export := range exports.List() {
// assert the export name is valid...
assert.NoError(t, validatePackageCatalogerExport(t, pkg, export))
// assert that config structs have a Default*Config functions to pair with them...
if strings.Contains(export.Name, "Config") && !strings.Contains(export.Name, "Default") {
// this is a config struct, make certain there is a pairing with a Default*Config function
assert.True(t, exports.Has("Default"+export.Name), "cataloger config struct %q in pkg %q must have a 'Default%s' function", export.Name, pkg, export.Name)
}
}
}
}
func validatePackageCatalogerExport(t *testing.T, pkg string, export exportToken) error {
constructorMatches, err := doublestar.Match("New*Cataloger", export.Name)
require.NoError(t, err)
defaultConfigMatches, err := doublestar.Match("Default*Config", export.Name)
require.NoError(t, err)
configMatches, err := doublestar.Match("*Config", export.Name)
require.NoError(t, err)
switch {
case constructorMatches:
if !export.isFunction() {
return fmt.Errorf("constructor convention used for non-function in pkg=%q: %#v", pkg, export)
}
returnTypes := strset.New(export.ReturnTypeNames...)
if !returnTypes.Has("pkg.Cataloger") {
return fmt.Errorf("constructor convention is to return pkg.Cataloger and not concrete types. pkg=%q constructor=%q types=%+v", pkg, export.Name, strings.Join(export.ReturnTypeNames, ","))
}
case defaultConfigMatches:
if !export.isFunction() {
return fmt.Errorf("default config convention used for non-function in pkg=%q: %#v", pkg, export)
}
if export.SignatureSize != 0 {
return fmt.Errorf("default config convention used for non-zero signature size in pkg=%q: %#v", pkg, export)
}
case configMatches:
if !export.isStruct() {
return fmt.Errorf("config convention used for non-struct in pkg=%q: %#v", pkg, export)
}
default:
return fmt.Errorf("unexpected export in pkg=%q: %#v", pkg, export)
}
return nil
}
type exportToken struct {
Name string
Type string
SignatureSize int
ReturnTypeNames []string
}
func (e exportToken) isFunction() bool {
return strings.Contains(e.Type, "ast.FuncType")
}
func (e exportToken) isStruct() bool {
return strings.Contains(e.Type, "ast.StructType")
}
type exportTokenSet map[string]exportToken
func (s exportTokenSet) Names() *strset.Set {
set := strset.New()
for k := range s {
set.Add(k)
}
return set
}
func (s exportTokenSet) Has(name string) bool {
_, ok := s[name]
return ok
}
func (s exportTokenSet) Add(tokens ...exportToken) {
for _, t := range tokens {
if _, ok := s[t.Name]; ok {
panic("duplicate token name: " + t.Name)
}
s[t.Name] = t
}
}
func (s exportTokenSet) Remove(names ...string) {
for _, name := range names {
delete(s, name)
}
}
func (s exportTokenSet) List() []exportToken {
var tokens []exportToken
for _, t := range s {
tokens = append(tokens, t)
}
return tokens
}
func packageCatalogerExports(t *testing.T) map[string]exportTokenSet {
t.Helper()
catalogerPath := filepath.Join(repoRoot(t), "syft", "pkg", "cataloger")
ignorePaths := []string{
filepath.Join(catalogerPath, "common"),
filepath.Join(catalogerPath, "generic"),
}
exportsPerPackage := make(map[string]exportTokenSet)
err := filepath.Walk(catalogerPath, func(path string, info os.FileInfo, err error) error {
require.NoError(t, err)
if info.IsDir() ||
!strings.HasSuffix(info.Name(), ".go") ||
strings.HasSuffix(info.Name(), "_test.go") ||
strings.Contains(path, "test-fixtures") ||
strings.Contains(path, "internal") {
return nil
}
for _, ignorePath := range ignorePaths {
if strings.Contains(path, ignorePath) {
return nil
}
}
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
require.NoError(t, err)
pkg := node.Name.Name
for _, f := range node.Decls {
switch decl := f.(type) {
case *ast.GenDecl:
for _, spec := range decl.Specs {
switch spec := spec.(type) {
case *ast.TypeSpec:
if spec.Name.IsExported() {
if _, ok := exportsPerPackage[pkg]; !ok {
exportsPerPackage[pkg] = make(exportTokenSet)
}
exportsPerPackage[pkg].Add(exportToken{
Name: spec.Name.Name,
Type: reflect.TypeOf(spec.Type).String(),
})
}
}
}
case *ast.FuncDecl:
if decl.Recv == nil && decl.Name.IsExported() {
var returnTypes []string
if decl.Type.Results != nil {
for _, field := range decl.Type.Results.List {
// TODO: there is probably a better way to extract the specific type name
//ty := strings.Join(strings.Split(fmt.Sprint(field.Type), " "), ".")
ty := types.ExprString(field.Type)
returnTypes = append(returnTypes, ty)
}
}
if _, ok := exportsPerPackage[pkg]; !ok {
exportsPerPackage[pkg] = make(exportTokenSet)
}
exportsPerPackage[pkg].Add(exportToken{
Name: decl.Name.Name,
Type: reflect.TypeOf(decl.Type).String(),
SignatureSize: len(decl.Type.Params.List),
ReturnTypeNames: returnTypes,
})
}
}
}
return nil
})
require.NoError(t, err)
// remove exceptions
// these are known violations to the common convention that are allowed.
if vs, ok := exportsPerPackage["binary"]; ok {
vs.Remove("Classifier", "EvidenceMatcher", "FileContentsVersionMatcher", "DefaultClassifiers")
}
return exportsPerPackage
}
@@ -1,204 +0,0 @@
package integration
import (
"bytes"
"go/ast"
"go/parser"
"go/token"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/task"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
)
func TestAllPackageCatalogersReachableInTasks(t *testing.T) {
// we want to see if we can get a task for all package catalogers. This is a bit tricky since we
// don't have a nice way to find all cataloger names in the codebase. Instead, we'll look at the
// count of unique task names from the package task factory set and compare that with the known constructors
// from a source analysis... they should match.
// additionally, at this time they should either have a "directory" or "image" tag as well. If there is no tag
// on a cataloger task then the test should fail.
taskFactories := task.DefaultPackageTaskFactories()
taskTagsByName := make(map[string][]string)
for _, factory := range taskFactories {
tsk := factory(task.DefaultCatalogingFactoryConfig())
if taskTagsByName[tsk.Name()] != nil {
t.Fatalf("duplicate task name: %q", tsk.Name())
}
require.NotNil(t, tsk)
if sel, ok := tsk.(task.Selector); ok {
taskTagsByName[tsk.Name()] = sel.Selectors()
} else {
taskTagsByName[tsk.Name()] = []string{}
}
}
var constructorCount int
constructorsPerPackage := getCatalogerConstructors(t)
for _, constructors := range constructorsPerPackage {
constructorCount += constructors.Size()
}
assert.Equal(t, len(taskTagsByName), constructorCount, "mismatch in number of cataloger constructors and task names")
for taskName, tags := range taskTagsByName {
if taskName == "sbom-cataloger" {
continue // this is a special case
}
if !strset.New(tags...).HasAny(pkgcataloging.ImageTag, pkgcataloging.DirectoryTag) {
t.Errorf("task %q is missing 'directory' or 'image' a tag", taskName)
}
}
}
func TestAllPackageCatalogersRepresentedInSource(t *testing.T) {
// find all functions in syft/pkg/cataloger/** that either:
// - match the name glob "New*Cataloger"
// - are in cataloger.go and match the name glob "New*"
//
// Then:
// - keep track of all packages with cataloger constructors
// - keep track of all constructors
constructorsPerPackage := getCatalogerConstructors(t)
// look at the source file in internal/task/package_tasks.go:
// - ensure all go packages that have constructors are imported
// - ensure there is a reference to all package constructors
assertAllPackageCatalogersRepresented(t, constructorsPerPackage)
}
func getCatalogerConstructors(t *testing.T) map[string]*strset.Set {
t.Helper()
root := repoRoot(t)
catalogerPath := filepath.Join(root, "syft", "pkg", "cataloger")
constructorsPerPackage := make(map[string]*strset.Set)
err := filepath.Walk(catalogerPath, func(path string, info os.FileInfo, err error) error {
require.NoError(t, err)
// ignore directories and test files...
if info.IsDir() || strings.HasSuffix(info.Name(), "_test.go") {
return nil
}
partialResults := getConstructorsFromExpectedFile(t, path, info)
constructorsPerPackage = mergeConstructors(constructorsPerPackage, partialResults)
partialResults = getCatalogerConstructorsFromPackage(t, path, info)
constructorsPerPackage = mergeConstructors(constructorsPerPackage, partialResults)
return nil
})
require.NoError(t, err)
// remove some exceptions
delete(constructorsPerPackage, "generic") // this is not an actual cataloger
return constructorsPerPackage
}
func getConstructorsFromExpectedFile(t *testing.T, path string, info os.FileInfo) map[string][]string {
constructorsPerPackage := make(map[string][]string)
if !strings.HasSuffix(info.Name(), "cataloger.go") && !strings.HasSuffix(info.Name(), "catalogers.go") {
return nil
}
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
require.NoError(t, err)
for _, f := range node.Decls {
fn, ok := f.(*ast.FuncDecl)
if !ok || fn.Recv != nil || !strings.HasPrefix(fn.Name.Name, "New") {
continue
}
pkg := node.Name.Name
constructorsPerPackage[pkg] = append(constructorsPerPackage[pkg], fn.Name.Name)
}
return constructorsPerPackage
}
func getCatalogerConstructorsFromPackage(t *testing.T, path string, info os.FileInfo) map[string][]string {
constructorsPerPackage := make(map[string][]string)
if info.IsDir() || !strings.HasSuffix(info.Name(), ".go") {
return nil
}
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
require.NoError(t, err)
for _, f := range node.Decls {
fn, ok := f.(*ast.FuncDecl)
if !ok || fn.Recv != nil || !strings.HasPrefix(fn.Name.Name, "New") || !strings.HasSuffix(fn.Name.Name, "Cataloger") {
continue
}
pkg := node.Name.Name
constructorsPerPackage[pkg] = append(constructorsPerPackage[pkg], fn.Name.Name)
}
return constructorsPerPackage
}
func assertAllPackageCatalogersRepresented(t *testing.T, constructorsPerPackage map[string]*strset.Set) {
t.Helper()
contents, err := os.ReadFile(filepath.Join(repoRoot(t), "internal", "task", "package_tasks.go"))
require.NoError(t, err)
// ensure all packages (keys) are represented in the package_tasks.go file
for pkg, constructors := range constructorsPerPackage {
if !assert.True(t, bytes.Contains(contents, []byte(pkg)), "missing package %q", pkg) {
continue
}
for _, constructor := range constructors.List() {
assert.True(t, bytes.Contains(contents, []byte(constructor)), "missing constructor %q for package %q", constructor, pkg)
}
}
}
func repoRoot(t testing.TB) string {
t.Helper()
root, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err != nil {
t.Fatalf("unable to find repo root dir: %+v", err)
}
absRepoRoot, err := filepath.Abs(strings.TrimSpace(string(root)))
if err != nil {
t.Fatal("unable to get abs path to repo root:", err)
}
return absRepoRoot
}
func mergeConstructors(constructorsPerPackage map[string]*strset.Set, partialResults map[string][]string) map[string]*strset.Set {
for pkg, constructors := range partialResults {
if _, ok := constructorsPerPackage[pkg]; !ok {
constructorsPerPackage[pkg] = strset.New()
}
constructorsPerPackage[pkg].Add(constructors...)
}
return constructorsPerPackage
}
@@ -1,26 +0,0 @@
package integration
import (
"fmt"
"testing"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/source"
)
func TestRelationshipsUnique(t *testing.T) {
// This test is to ensure that the relationships are deduplicated in the final SBOM.
// It is not a test of the relationships themselves.
// This test is a regression test for #syft/2509
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope)
observedRelationships := strset.New()
for _, rel := range sbom.Relationships {
unique := fmt.Sprintf("%s:%s:%s", rel.From.ID(), rel.To.ID(), rel.Type)
if observedRelationships.Has(unique) {
t.Errorf("duplicate relationship found: %s", unique)
}
observedRelationships.Add(unique)
}
}
@@ -1,50 +0,0 @@
package integration
import (
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
func TestSbomCataloger(t *testing.T) {
assertCount := func(t *testing.T, sbom sbom.SBOM, expectedGoModCatalogerPkgs int, expectedSbomCatalogerPkgs int) {
actualSbomPkgs := 0
actualGoModPkgs := 0
for p := range sbom.Artifacts.Packages.Enumerate(pkg.GoModulePkg) {
if p.FoundBy == "go-module-file-cataloger" {
actualGoModPkgs += 1
} else if p.FoundBy == "sbom-cataloger" {
actualSbomPkgs += 1
}
}
if actualGoModPkgs != expectedGoModCatalogerPkgs {
t.Errorf("unexpected number of packages from go mod cataloger: %d != %d", expectedGoModCatalogerPkgs, actualGoModPkgs)
}
if actualSbomPkgs != expectedSbomCatalogerPkgs {
t.Errorf("unexpected number of packages from sbom cataloger: %d != %d", expectedSbomCatalogerPkgs, actualSbomPkgs)
}
}
t.Run("default catalogers", func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-sbom-cataloger", source.SquashedScope, "+go-module-file-cataloger")
expectedSbomCatalogerPkgs := 0
expectedGoModCatalogerPkgs := 2
assertCount(t, sbom, expectedGoModCatalogerPkgs, expectedSbomCatalogerPkgs)
})
// The image contains a go.mod file with 2 dependencies and an spdx json sbom.
// The go.mod file contains 2 dependencies, and the sbom includes a go dependency
// that overlaps with the go.mod
t.Run("with sbom cataloger", func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-sbom-cataloger", source.SquashedScope, "+go-module-file-cataloger", "+sbom-cataloger")
expectedSbomCatalogerPkgs := 1
expectedGoModCatalogerPkgs := 2
assertCount(t, sbom, expectedGoModCatalogerPkgs, expectedSbomCatalogerPkgs)
})
}
+14 -12
View File
@@ -16,35 +16,32 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
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/stereoscope v0.0.0-20240118133533-eb656fc71793
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54
// 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
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
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/lipgloss v0.9.1
github.com/dave/jennifer v1.7.0
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/distribution/reference v0.5.0
github.com/docker/docker v25.0.1+incompatible
github.com/docker/docker v24.0.7+incompatible
github.com/dustin/go-humanize v1.0.1
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.2.0
github.com/gkampitakis/go-snaps v0.5.0
github.com/gkampitakis/go-snaps v0.4.12
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.11.0
github.com/go-test/deep v1.1.0
github.com/google/go-cmp v0.6.0
github.com/google/go-containerregistry v0.18.0
github.com/google/go-containerregistry v0.17.0
github.com/google/licensecheck v0.3.1
github.com/google/uuid v1.6.0
github.com/google/uuid v1.5.0
github.com/gookit/color v1.5.4
github.com/hashicorp/go-multierror v1.1.1
github.com/iancoleman/strcase v0.3.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/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
@@ -73,13 +70,18 @@ require (
github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
golang.org/x/mod v0.14.0
golang.org/x/net v0.20.0
gopkg.in/yaml.v3 v3.0.1
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 (
dario.cat/mergo v1.0.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
@@ -99,7 +101,7 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/containerd v1.7.11 // indirect
@@ -151,7 +153,7 @@ require (
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/maruel/natural v1.1.1 // indirect
github.com/maruel/natural v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
@@ -181,7 +183,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
+17 -33
View File
@@ -107,8 +107,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8=
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.0-20240118133533-eb656fc71793 h1:wji+qdjsV7ooolBwb3faVZnEK3WtY/kcT5473kxVZS4=
github.com/anchore/stereoscope v0.0.0-20240118133533-eb656fc71793/go.mod h1:IylG7ofLoUKHwS1XDF6rPhOmaE3GgpAgsMdvvYfooTU=
github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54 h1:i2YK5QEs9H2YB3B2zv+AGR44ves0nmAGOD07lMphH14=
github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54/go.mod h1:IylG7ofLoUKHwS1XDF6rPhOmaE3GgpAgsMdvvYfooTU=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@@ -142,8 +142,6 @@ github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTS
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -165,9 +163,8 @@ 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/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
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.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-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -223,8 +220,8 @@ github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qe
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v25.0.1+incompatible h1:k5TYd5rIVQRSqcTwCID+cyVA0yRg86+Pcrz1ls0/frA=
github.com/docker/docker v25.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -283,8 +280,8 @@ github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvp
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.0 h1:Qpk1+V1WJszsDvYMgrI15XbUvWqpsFvFxgaXKV6RU9Q=
github.com/gkampitakis/go-snaps v0.5.0/go.mod h1:gX47VYsnvqHsjBrTrpaXfm1Dm1Xg+nREZPKRT8xifV8=
github.com/gkampitakis/go-snaps v0.4.12 h1:YeMgKOm0XW3f/Pt2rYpUlpyF8nG6lYGe9oXFJw5LdME=
github.com/gkampitakis/go-snaps v0.4.12/go.mod h1:PpnF1KPXQAHBdb/DHoi/1VmlwE+ZkVHzl+QHmgzMSz8=
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
@@ -375,8 +372,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.18.0 h1:ShE7erKNPqRh5ue6Z9DUOlk04WsnFWPO6YGr3OxnfoQ=
github.com/google/go-containerregistry v0.18.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
github.com/google/go-containerregistry v0.17.0 h1:5p+zYs/R4VGHkhyvgWurWrpJ2hW4Vv9fQI+GzdcwXLk=
github.com/google/go-containerregistry v0.17.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
@@ -404,8 +401,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S3
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@@ -413,10 +410,7 @@ github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -523,8 +517,8 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -673,8 +667,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -837,25 +831,17 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZ
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
@@ -1283,8 +1269,6 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+1 -1
View File
@@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "14.0.0"
JSONSchemaVersion = "13.0.0"
)
+1 -2
View File
@@ -22,7 +22,6 @@ func supportedHashAlgorithms() []crypto.Hash {
}
func NewDigestsFromFile(closer io.ReadCloser, hashes []crypto.Hash) ([]file.Digest, error) {
hashes = NormalizeHashes(hashes)
// create a set of hasher objects tied together with a single writer to feed content into
hashers := make([]hash.Hash, len(hashes))
writers := make([]io.Writer, len(hashes))
@@ -68,7 +67,7 @@ func Hashers(names ...string) ([]crypto.Hash, error) {
}
hashers = append(hashers, hashObj)
}
return NormalizeHashes(hashers), nil
return hashers, nil
}
func CleanDigestAlgorithmName(name string) string {
-24
View File
@@ -1,24 +0,0 @@
package file
import (
"crypto"
"sort"
"github.com/scylladb/go-set/uset"
)
func NormalizeHashes(hashes []crypto.Hash) []crypto.Hash {
set := uset.New()
for _, h := range hashes {
set.Add(uint(h))
}
list := set.List()
sort.Slice(list, func(i, j int) bool {
return list[i] < list[j]
})
result := make([]crypto.Hash, len(list))
for i, v := range list {
result[i] = crypto.Hash(v)
}
return result
}
-44
View File
@@ -1,44 +0,0 @@
package file
import (
"crypto"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNormalizeHashes(t *testing.T) {
tests := []struct {
name string
input []crypto.Hash
want []crypto.Hash
}{
{
name: "deduplicate hashes",
input: []crypto.Hash{
crypto.SHA1,
crypto.SHA1,
},
want: []crypto.Hash{
crypto.SHA1,
},
},
{
name: "sort hashes",
input: []crypto.Hash{
crypto.SHA512,
crypto.SHA1,
},
want: []crypto.Hash{
crypto.SHA1,
crypto.SHA512,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, NormalizeHashes(tt.input))
})
}
}
+3 -3
View File
@@ -30,7 +30,7 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
return nil
}
result, err := digestsCataloger.Catalog(ctx, resolver, coordinates...)
result, err := digestsCataloger.Catalog(resolver, coordinates...)
if err != nil {
return fmt.Errorf("unable to catalog file digests: %w", err)
}
@@ -60,7 +60,7 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
return nil
}
result, err := metadataCataloger.Catalog(ctx, resolver, coordinates...)
result, err := metadataCataloger.Catalog(resolver, coordinates...)
if err != nil {
return err
}
@@ -85,7 +85,7 @@ func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
accessor := builder.(sbomsync.Accessor)
result, err := cat.Catalog(ctx, resolver)
result, err := cat.Catalog(resolver)
if err != nil {
return err
}
+1 -1
View File
@@ -100,7 +100,7 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
t := bus.StartCatalogerTask(info, -1, "")
pkgs, relationships, err := c.Catalog(ctx, resolver)
pkgs, relationships, err := c.Catalog(resolver)
if err != nil {
return fmt.Errorf("unable to catalog packages with %q: %w", c.Name(), err)
}
+2 -10
View File
@@ -18,7 +18,6 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
"github.com/anchore/syft/syft/pkg/cataloger/nix"
"github.com/anchore/syft/syft/pkg/cataloger/php"
"github.com/anchore/syft/syft/pkg/cataloger/python"
@@ -113,16 +112,9 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return binary.NewCataloger(cfg.PackagesConfig.Binary)
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary",
),
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary"),
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return kernel.NewLinuxKernelCataloger(cfg.PackagesConfig.LinuxKernel)
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "linux", "kernel",
),
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, "sbom"), // note: not evidence of installed packages
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, pkgcataloging.ImageTag, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "sbom"), // note: not evidence of installed packages
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -73,6 +73,6 @@ func (cfg Config) WithSelection(selection file.Selection) Config {
}
func (cfg Config) WithHashers(hashers []crypto.Hash) Config {
cfg.Hashers = intFile.NormalizeHashes(hashers)
cfg.Hashers = hashers
return cfg
}
+1 -2
View File
@@ -20,9 +20,8 @@ type Config struct {
func DefaultConfig() Config {
return Config{
Binary: binary.DefaultCatalogerConfig(),
Golang: golang.DefaultCatalogerConfig(),
LinuxKernel: kernel.DefaultLinuxKernelCatalogerConfig(),
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
Python: python.DefaultCatalogerConfig(),
JavaArchive: java.DefaultArchiveCatalogerConfig(),
}
+11 -9
View File
@@ -2,11 +2,13 @@ package cpe
import (
"sort"
"github.com/facebookincubator/nvdtools/wfn"
)
var _ sort.Interface = (*BySpecificity)(nil)
type BySpecificity []CPE
type BySpecificity []wfn.Attributes
func (c BySpecificity) Len() int { return len(c) }
@@ -33,17 +35,17 @@ func (c BySpecificity) Less(i, j int) bool {
return c[i].BindToFmtString() < c[j].BindToFmtString()
}
func countFieldLength(cpe CPE) int {
func countFieldLength(cpe wfn.Attributes) int {
return len(cpe.Part + cpe.Vendor + cpe.Product + cpe.Version + cpe.TargetSW)
}
func weightedCountForSpecifiedFields(cpe CPE) int {
checksForSpecifiedField := []func(cpe CPE) (bool, int){
func(cpe CPE) (bool, int) { return cpe.Part != "", 2 },
func(cpe CPE) (bool, int) { return cpe.Vendor != "", 3 },
func(cpe CPE) (bool, int) { return cpe.Product != "", 4 },
func(cpe CPE) (bool, int) { return cpe.Version != "", 1 },
func(cpe CPE) (bool, int) { return cpe.TargetSW != "", 1 },
func weightedCountForSpecifiedFields(cpe wfn.Attributes) int {
checksForSpecifiedField := []func(cpe wfn.Attributes) (bool, int){
func(cpe wfn.Attributes) (bool, int) { return cpe.Part != "", 2 },
func(cpe wfn.Attributes) (bool, int) { return cpe.Vendor != "", 3 },
func(cpe wfn.Attributes) (bool, int) { return cpe.Product != "", 4 },
func(cpe wfn.Attributes) (bool, int) { return cpe.Version != "", 1 },
func(cpe wfn.Attributes) (bool, int) { return cpe.TargetSW != "", 1 },
}
weightedCount := 0
+15 -45
View File
@@ -8,35 +8,7 @@ import (
"github.com/facebookincubator/nvdtools/wfn"
)
type CPE struct {
Part string
Vendor string
Product string
Version string
Update string
Edition string
SWEdition string
TargetSW string
TargetHW string
Other string
Language string
}
func (c CPE) asAttributes() wfn.Attributes {
return wfn.Attributes(c)
}
func fromAttributes(a wfn.Attributes) CPE {
return CPE(a)
}
func (c CPE) BindToFmtString() string {
return c.asAttributes().BindToFmtString()
}
func NewWithAny() CPE {
return fromAttributes(*(wfn.NewAttributesWithAny()))
}
type CPE = wfn.Attributes
const (
allowedCPEPunctuation = "-!\"#$%&'()+,./:;<=>@[]^`{|}~"
@@ -62,7 +34,7 @@ func New(cpeStr string) (CPE, error) {
}
// ensure that this CPE can be validated after being fully sanitized
if ValidateString(c.String()) != nil {
if ValidateString(String(c)) != nil {
return CPE{}, err
}
@@ -99,22 +71,20 @@ func newWithoutValidation(cpeStr string) (CPE, error) {
return CPE{}, fmt.Errorf("failed to parse CPE=%q", cpeStr)
}
syftCPE := fromAttributes(*value)
// we need to compare the raw data since we are constructing CPEs in other locations
syftCPE.Vendor = normalizeField(syftCPE.Vendor)
syftCPE.Product = normalizeField(syftCPE.Product)
syftCPE.Language = normalizeField(syftCPE.Language)
syftCPE.Version = normalizeField(syftCPE.Version)
syftCPE.TargetSW = normalizeField(syftCPE.TargetSW)
syftCPE.Part = normalizeField(syftCPE.Part)
syftCPE.Edition = normalizeField(syftCPE.Edition)
syftCPE.Other = normalizeField(syftCPE.Other)
syftCPE.SWEdition = normalizeField(syftCPE.SWEdition)
syftCPE.TargetHW = normalizeField(syftCPE.TargetHW)
syftCPE.Update = normalizeField(syftCPE.Update)
value.Vendor = normalizeField(value.Vendor)
value.Product = normalizeField(value.Product)
value.Language = normalizeField(value.Language)
value.Version = normalizeField(value.Version)
value.TargetSW = normalizeField(value.TargetSW)
value.Part = normalizeField(value.Part)
value.Edition = normalizeField(value.Edition)
value.Other = normalizeField(value.Other)
value.SWEdition = normalizeField(value.SWEdition)
value.TargetHW = normalizeField(value.TargetHW)
value.Update = normalizeField(value.Update)
return syftCPE, nil
return *value, nil
}
func normalizeField(field string) string {
@@ -142,7 +112,7 @@ func stripSlashes(s string) string {
return sb.String()
}
func (c CPE) String() string {
func String(c CPE) string {
output := CPE{}
output.Vendor = sanitize(c.Vendor)
output.Product = sanitize(c.Product)
+8 -9
View File
@@ -7,7 +7,6 @@ import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -42,8 +41,8 @@ func Test_New(t *testing.T) {
t.Fatalf("got an error while creating CPE: %+v", err)
}
if d := cmp.Diff(actual, test.expected); d != "" {
t.Errorf("CPE mismatch (-want +got):\n%s", d)
if String(actual) != String(test.expected) {
t.Errorf("mismatched entries:\n\texpected:%+v\n\t actual:%+v\n", String(test.expected), String(actual))
}
})
@@ -99,7 +98,7 @@ func Test_CPEParser(t *testing.T) {
assert.Equal(t, c1, c2)
assert.Equal(t, c1, test.WFN)
assert.Equal(t, c2, test.WFN)
assert.Equal(t, test.WFN.String(), test.CPEString)
assert.Equal(t, String(test.WFN), test.CPEString)
})
}
}
@@ -165,12 +164,12 @@ func Test_InvalidCPE(t *testing.T) {
if test.expectedErr {
assert.Error(t, err)
if t.Failed() {
t.Logf("got CPE: %q details: %+v", c, c)
t.Logf("got CPE: %q details: %+v", String(c), c)
}
return
}
require.NoError(t, err)
assert.Equal(t, test.expected, c.String())
assert.Equal(t, test.expected, String(c))
})
}
}
@@ -216,13 +215,13 @@ func Test_RoundTrip(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// CPE string must be preserved through a round trip
assert.Equal(t, test.cpe, Must(test.cpe).String())
assert.Equal(t, test.cpe, String(Must(test.cpe)))
// The parsed CPE must be the same after a round trip
assert.Equal(t, Must(test.cpe), Must(Must(test.cpe).String()))
assert.Equal(t, Must(test.cpe), Must(String(Must(test.cpe))))
// The test case parsed CPE must be the same after parsing the input string
assert.Equal(t, test.parsedCPE, Must(test.cpe))
// The test case parsed CPE must produce the same string as the input cpe
assert.Equal(t, test.parsedCPE.String(), test.cpe)
assert.Equal(t, String(test.parsedCPE), test.cpe)
})
}
}
+3
View File
@@ -11,6 +11,7 @@ import (
"github.com/wagoodman/go-progress"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/relationship"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/internal/task"
"github.com/anchore/syft/syft/artifact"
@@ -77,6 +78,8 @@ func CreateSBOM(ctx context.Context, src source.Source, cfg *CreateSBOMConfig) (
packageCatalogingProgress.SetCompleted()
catalogingProgress.SetCompleted()
relationship.Finalize(builder, cfg.Relationships, src)
return &s, nil
}
+1 -2
View File
@@ -1,7 +1,6 @@
package syft
import (
"context"
"sort"
"testing"
@@ -35,7 +34,7 @@ func (d dummyCataloger) Name() string {
return d.name
}
func (d dummyCataloger) Catalog(_ context.Context, _ file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
func (d dummyCataloger) Catalog(_ file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
return nil, nil, nil
}
+1 -2
View File
@@ -2,7 +2,6 @@ package filecontent
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
@@ -43,7 +42,7 @@ func NewCataloger(cfg Config) *Cataloger {
}
}
func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file.Coordinates]string, error) {
func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]string, error) {
results := make(map[file.Coordinates]string)
var locations []file.Location
@@ -1,7 +1,6 @@
package filecontent
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
@@ -74,7 +73,7 @@ func TestContentsCataloger(t *testing.T) {
})
resolver := file.NewMockResolverForPaths(test.files...)
actual, err := c.Catalog(context.Background(), resolver)
actual, err := c.Catalog(resolver)
assert.NoError(t, err)
assert.Equal(t, test.expected, actual, "mismatched contents")
+3 -4
View File
@@ -1,7 +1,6 @@
package filedigest
import (
"context"
"crypto"
"errors"
"fmt"
@@ -26,16 +25,16 @@ type Cataloger struct {
func NewCataloger(hashes []crypto.Hash) *Cataloger {
return &Cataloger{
hashes: intFile.NormalizeHashes(hashes),
hashes: hashes,
}
}
func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates][]file.Digest, error) {
func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates][]file.Digest, error) {
results := make(map[file.Coordinates][]file.Digest)
var locations []file.Location
if len(coordinates) == 0 {
locations = intCataloger.AllRegularFiles(ctx, resolver)
locations = intCataloger.AllRegularFiles(resolver)
} else {
for _, c := range coordinates {
locs, err := resolver.FilesByPath(c.RealPath)
@@ -1,7 +1,6 @@
package filedigest
import (
"context"
"crypto"
"fmt"
"io"
@@ -83,7 +82,7 @@ func TestDigestsCataloger(t *testing.T) {
resolver, err := src.FileResolver(source.SquashedScope)
require.NoError(t, err)
actual, err := c.Catalog(context.Background(), resolver)
actual, err := c.Catalog(resolver)
require.NoError(t, err)
assert.Equal(t, test.expected, actual, "mismatched digests")
@@ -139,7 +138,7 @@ func TestDigestsCataloger_MixFileTypes(t *testing.T) {
t.Run(test.path, func(t *testing.T) {
c := NewCataloger([]crypto.Hash{crypto.MD5})
actual, err := c.Catalog(context.Background(), resolver)
actual, err := c.Catalog(resolver)
if err != nil {
t.Fatalf("could not catalog: %+v", err)
}
@@ -196,7 +195,7 @@ func TestFileDigestCataloger_GivenCoordinates(t *testing.T) {
// note: an important difference between this test and the previous is that this test is using a list
// of specific coordinates to catalog
actual, err := c.Catalog(context.Background(), resolver, l.Coordinates)
actual, err := c.Catalog(resolver, l.Coordinates)
require.NoError(t, err)
require.Len(t, actual, 1)
+3 -11
View File
@@ -1,7 +1,6 @@
package filemetadata
import (
"context"
"fmt"
"github.com/dustin/go-humanize"
@@ -19,13 +18,11 @@ func NewCataloger() *Cataloger {
return &Cataloger{}
}
func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates]file.Metadata, error) {
func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates]file.Metadata, error) {
results := make(map[file.Coordinates]file.Metadata)
var locations <-chan file.Location
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if len(coordinates) == 0 {
locations = resolver.AllLocations(ctx)
locations = resolver.AllLocations()
} else {
locations = func() <-chan file.Location {
ch := make(chan file.Location)
@@ -38,12 +35,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
continue
}
for _, loc := range locs {
select {
case <-ctx.Done():
return
case ch <- loc:
continue
}
ch <- loc
}
}
}()
@@ -1,7 +1,6 @@
package filemetadata
import (
"context"
"os"
"testing"
@@ -27,7 +26,7 @@ func TestFileMetadataCataloger(t *testing.T) {
resolver, err := src.FileResolver(source.SquashedScope)
require.NoError(t, err)
actual, err := c.Catalog(context.Background(), resolver)
actual, err := c.Catalog(resolver)
require.NoError(t, err)
tests := []struct {
@@ -197,7 +196,7 @@ func TestFileMetadataCataloger_GivenCoordinates(t *testing.T) {
// note: an important difference between this test and the previous is that this test is using a list
// of specific coordinates to catalog
actual, err := c.Catalog(context.Background(), resolver, l.Coordinates)
actual, err := c.Catalog(resolver, l.Coordinates)
require.NoError(t, err)
require.Len(t, actual, 1)
@@ -1,17 +1,13 @@
package internal
import (
"context"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
)
func AllRegularFiles(ctx context.Context, resolver file.Resolver) (locations []file.Location) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
for location := range resolver.AllLocations(ctx) {
func AllRegularFiles(resolver file.Resolver) (locations []file.Location) {
for location := range resolver.AllLocations() {
resolvedLocations, err := resolver.FilesByPath(location.RealPath)
if err != nil {
log.Warnf("unable to resolve %+v: %+v", location, err)
@@ -1,7 +1,6 @@
package internal
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
@@ -55,7 +54,7 @@ func Test_allRegularFiles(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resolver := tt.setup()
locations := AllRegularFiles(context.Background(), resolver)
locations := AllRegularFiles(resolver)
realLocations := strset.New()
virtualLocations := strset.New()
for _, l := range locations {
+2 -8
View File
@@ -1,7 +1,6 @@
package file
import (
"context"
"fmt"
"io"
"os"
@@ -145,17 +144,12 @@ func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location {
return &paths[0]
}
func (r MockResolver) AllLocations(ctx context.Context) <-chan Location {
func (r MockResolver) AllLocations() <-chan Location {
results := make(chan Location)
go func() {
defer close(results)
for _, l := range r.locations {
select {
case <-ctx.Done():
return
case results <- l:
continue
}
results <- l
}
}()
return results
+2 -5
View File
@@ -1,9 +1,6 @@
package file
import (
"context"
"io"
)
import "io"
// Resolver is an interface that encompasses how to get specific file references and file contents for a generic data source.
type Resolver interface {
@@ -56,7 +53,7 @@ type LocationResolver interface {
// The implementation for this may vary, however, generally the following considerations should be made:
// - NO symlink resolution should be performed on results
// - returns locations for any file or directory
AllLocations(ctx context.Context) <-chan Location
AllLocations() <-chan Location
}
type WritableResolver interface {
+2 -2
View File
@@ -12,7 +12,7 @@ func encodeSingleCPE(p pkg.Package) string {
// Since the CPEs in a package are sorted by specificity
// we can extract the first CPE as the one to output in cyclonedx
if len(p.CPEs) > 0 {
return p.CPEs[0].String()
return cpe.String(p.CPEs[0])
}
return ""
}
@@ -25,7 +25,7 @@ func encodeCPEs(p pkg.Package) (out []cyclonedx.Property) {
}
out = append(out, cyclonedx.Property{
Name: "syft:cpe23",
Value: c.String(),
Value: cpe.String(c),
})
}
return

Some files were not shown because too many files have changed in this diff Show More