diff --git a/docs/docs/kubernetes/scanning.md b/docs/docs/kubernetes/scanning.md index 62c00634da..7c3e838a54 100644 --- a/docs/docs/kubernetes/scanning.md +++ b/docs/docs/kubernetes/scanning.md @@ -29,7 +29,7 @@ $ trivy k8s -n default --severity CRITICAL Scan a cluster and generate a simple summary report. The only outputs currently supported are `all` and `summary`. The default report format is `summary` ``` -$ trivy k8s +$ trivy k8s ``` ![k8s Summary Report](../../imgs/k8s-summary.png) diff --git a/go.mod b/go.mod index 30f51a779d..c94d2e4163 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 - github.com/aquasecurity/trivy-db v0.0.0-20220327074450-74195d9604b2 + github.com/aquasecurity/trivy-db v0.0.0-20220510190819-8ca06716f46e github.com/caarlos0/env/v6 v6.9.1 github.com/cenkalti/backoff v2.2.1+incompatible github.com/cheggaaa/pb/v3 v3.0.8 @@ -46,6 +46,7 @@ require ( golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 google.golang.org/protobuf v1.28.0 + gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/utils v0.0.0-20211116205334-6203023598ed ) @@ -71,8 +72,6 @@ require ( github.com/Microsoft/go-winio v0.5.1 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/VividCortex/ewma v1.1.1 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect @@ -96,31 +95,19 @@ require ( github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/emirpasic/gods v1.12.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-errors/errors v1.0.1 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect github.com/go-git/go-git/v5 v5.4.2 // indirect - github.com/go-logr/logr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.14 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-yaml v1.8.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/btree v1.0.1 // indirect - github.com/google/go-cmp v0.5.7 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -128,11 +115,8 @@ require ( github.com/hashicorp/hcl/v2 v2.12.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/klauspost/compress v1.15.1 // indirect @@ -142,9 +126,7 @@ require ( github.com/liamg/jfather v0.0.7 // indirect github.com/liamg/memoryfs v1.4.1 // indirect github.com/liamg/tml v0.6.0 - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.5 // indirect - github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -157,15 +139,11 @@ require ( github.com/moby/sys/mount v0.3.0 // indirect github.com/moby/sys/mountinfo v0.6.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect github.com/opencontainers/runc v1.1.1 // indirect github.com/owenrumney/squealer v1.0.1-0.20220510063705-c0be93f0edea // indirect - github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect @@ -178,21 +156,17 @@ require ( github.com/sirupsen/logrus v1.8.1 // indirect github.com/spdx/tools-golang v0.3.0 github.com/spf13/cast v1.4.1 // indirect - github.com/spf13/cobra v1.4.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.3.0 // indirect github.com/ulikunitz/xz v0.5.8 // indirect github.com/vbatts/tar-split v0.11.2 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect github.com/zclconf/go-cty v1.10.0 // indirect github.com/zclconf/go-cty-yaml v1.0.2 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.23.0 // indirect - go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd @@ -202,7 +176,6 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/tools v0.1.8 // indirect google.golang.org/api v0.62.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -210,14 +183,7 @@ require ( google.golang.org/grpc v1.46.0 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/go-playground/validator.v9 v9.31.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/api v0.23.6 // indirect - k8s.io/apimachinery v0.23.6 // indirect - k8s.io/cli-runtime v0.23.6 // indirect - k8s.io/client-go v0.23.6 // indirect - k8s.io/klog/v2 v2.30.0 // indirect - k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect lukechampine.com/uint128 v1.1.1 // indirect modernc.org/cc/v3 v3.35.22 // indirect modernc.org/ccgo/v3 v3.15.1 // indirect @@ -228,6 +194,51 @@ require ( modernc.org/sqlite v1.14.5 // indirect modernc.org/strutil v1.1.1 // indirect modernc.org/token v1.0.0 // indirect +) + +require ( + github.com/aquasecurity/table v1.5.1 + github.com/aquasecurity/trivy-kubernetes v0.1.0 +) + +require ( + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/alecthomas/chroma v0.10.0 // indirect + github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/spf13/cobra v1.4.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect + go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/api v0.23.6 // indirect + k8s.io/apimachinery v0.23.6 // indirect + k8s.io/cli-runtime v0.23.6 // indirect + k8s.io/client-go v0.23.6 // indirect + k8s.io/klog/v2 v2.30.0 // indirect + k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect sigs.k8s.io/kustomize/api v0.10.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.0 // indirect @@ -235,17 +246,6 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect ) -require gopkg.in/yaml.v2 v2.4.0 - -require github.com/aquasecurity/trivy-kubernetes v0.1.0 - -require github.com/aquasecurity/table v1.5.1 - -require ( - github.com/alecthomas/chroma v0.10.0 // indirect - github.com/dlclark/regexp2 v1.4.0 // indirect -) - // To resolve CVE-2022-23648 replace github.com/containerd/containerd v1.5.9 => github.com/containerd/containerd v1.5.10 diff --git a/go.sum b/go.sum index 3f89153fe8..ef4043220a 100644 --- a/go.sum +++ b/go.sum @@ -198,8 +198,8 @@ github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1: github.com/aquasecurity/table v1.5.1 h1:y05AuHM3p4BGybbGn/XbcTX3RxpyzeTXAXYMcJve4IE= github.com/aquasecurity/table v1.5.1/go.mod h1:1MFKrEPJ8NchM917BrVGvsqoXJo1OL1Ja7dF3PgUea4= github.com/aquasecurity/testdocker v0.0.0-20210911155206-e1e85f5a1516 h1:moQmzbpLo5dxHQCyEhqzizsDSNrNhn/7uRTCZzo4A1o= -github.com/aquasecurity/trivy-db v0.0.0-20220327074450-74195d9604b2 h1:q2Gza4V8uO5C1COzC2HeTbQgJIrmC6dTWaXZ8ujiWu0= -github.com/aquasecurity/trivy-db v0.0.0-20220327074450-74195d9604b2/go.mod h1:EwiQRdzVq6k7cKOMjkss8LjWMt2FUW7NaYwE7HfZZvk= +github.com/aquasecurity/trivy-db v0.0.0-20220510190819-8ca06716f46e h1:NLm5KWGcnkwaUR1GODPePyhNsbuFiT6lgKYcCcW9c10= +github.com/aquasecurity/trivy-db v0.0.0-20220510190819-8ca06716f46e/go.mod h1:/nULgnDeq/JMPMVwE1dmf4kWlYn++7VrM3O2naj4BHA= github.com/aquasecurity/trivy-kubernetes v0.1.0 h1:eE7JSdqo83Kn87c86DcUIsPAtW0K9UnkkHEQ4sGI030= github.com/aquasecurity/trivy-kubernetes v0.1.0/go.mod h1:9fU3sHz/wXN5ruZ5snUEJpzm2X6pUndKucv1mz9Walc= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= diff --git a/pkg/commands/artifact/config.go b/pkg/commands/artifact/config.go index 5e13c17e70..66192a5ebc 100644 --- a/pkg/commands/artifact/config.go +++ b/pkg/commands/artifact/config.go @@ -10,7 +10,7 @@ import ( // ConfigRun runs scan on config files func ConfigRun(ctx *cli.Context) error { - opt, err := initOption(ctx) + opt, err := InitOption(ctx) if err != nil { return xerrors.Errorf("option error: %w", err) } @@ -22,9 +22,6 @@ func ConfigRun(ctx *cli.Context) error { opt.VulnType = nil opt.SecurityChecks = []string{types.SecurityCheckConfig} - // Skip downloading vulnerability DB - opt.SkipDBUpdate = true - // Run filesystem command internally - return Run(ctx.Context, opt, filesystemStandaloneScanner, initCache) + return run(ctx.Context, opt, filesystemArtifact) } diff --git a/pkg/commands/artifact/fs.go b/pkg/commands/artifact/fs.go index 5c49aeb470..e66840742d 100644 --- a/pkg/commands/artifact/fs.go +++ b/pkg/commands/artifact/fs.go @@ -6,7 +6,6 @@ import ( "github.com/urfave/cli/v2" "golang.org/x/xerrors" - "github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/scanner" ) @@ -30,39 +29,10 @@ func filesystemRemoteScanner(ctx context.Context, conf ScannerConfig) (scanner.S // FilesystemRun runs scan on filesystem for language-specific dependencies and config files func FilesystemRun(ctx *cli.Context) error { - opt, err := initOption(ctx) - if err != nil { - return xerrors.Errorf("option error: %w", err) - } - - // Disable the individual package scanning - opt.DisabledAnalyzers = analyzer.TypeIndividualPkgs - //opt.DisabledAnalyzers = append(opt.DisabledAnalyzers, analyzer.TypeSecret) - - // client/server mode - if opt.RemoteAddr != "" { - return Run(ctx.Context, opt, filesystemRemoteScanner, initCache) - } - - // standalone mode - return Run(ctx.Context, opt, filesystemStandaloneScanner, initCache) + return Run(ctx, filesystemArtifact) } // RootfsRun runs scan on rootfs. func RootfsRun(ctx *cli.Context) error { - opt, err := initOption(ctx) - if err != nil { - return xerrors.Errorf("option error: %w", err) - } - - // Disable the lock file scanning - opt.DisabledAnalyzers = analyzer.TypeLockfiles - - // client/server mode - if opt.RemoteAddr != "" { - return Run(ctx.Context, opt, filesystemRemoteScanner, initCache) - } - - // standalone mode - return Run(ctx.Context, opt, filesystemStandaloneScanner, initCache) + return Run(ctx, rootfsArtifact) } diff --git a/pkg/commands/artifact/image.go b/pkg/commands/artifact/image.go index 87e850380b..ec001b250c 100644 --- a/pkg/commands/artifact/image.go +++ b/pkg/commands/artifact/image.go @@ -6,14 +6,13 @@ import ( "github.com/urfave/cli/v2" "golang.org/x/xerrors" - "github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/scanner" "github.com/aquasecurity/trivy/pkg/types" ) -// imageScanner initializes a container image scanner in standalone mode +// imageStandaloneScanner initializes a container image scanner in standalone mode // $ trivy image alpine:3.15 -func imageScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { +func imageStandaloneScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { dockerOpt, err := types.GetDockerOption(conf.ArtifactOption.InsecureSkipTLS) if err != nil { return scanner.Scanner{}, nil, err @@ -26,9 +25,9 @@ func imageScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, fun return s, cleanup, nil } -// archiveScanner initializes an image archive scanner in standalone mode +// archiveStandaloneScanner initializes an image archive scanner in standalone mode // $ trivy image --input alpine.tar -func archiveScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { +func archiveStandaloneScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { s, err := initializeArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, conf.ArtifactOption) if err != nil { return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err) @@ -36,9 +35,9 @@ func archiveScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, f return s, func() {}, nil } -// remoteImageScanner initializes a container image scanner in client/server mode +// imageRemoteScanner initializes a container image scanner in client/server mode // $ trivy image --server localhost:4954 alpine:3.15 -func remoteImageScanner(ctx context.Context, conf ScannerConfig) ( +func imageRemoteScanner(ctx context.Context, conf ScannerConfig) ( scanner.Scanner, func(), error) { // Scan an image in Docker Engine, Docker Registry, etc. dockerOpt, err := types.GetDockerOption(conf.ArtifactOption.InsecureSkipTLS) @@ -54,9 +53,9 @@ func remoteImageScanner(ctx context.Context, conf ScannerConfig) ( return s, cleanup, nil } -// remoteArchiveScanner initializes an image archive scanner in client/server mode +// archiveRemoteScanner initializes an image archive scanner in client/server mode // $ trivy image --server localhost:4954 --input alpine.tar -func remoteArchiveScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { +func archiveRemoteScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { // Scan tar file s, err := initializeRemoteArchiveScanner(ctx, conf.Target, conf.ArtifactCache, conf.RemoteOption, conf.ArtifactOption) if err != nil { @@ -67,43 +66,5 @@ func remoteArchiveScanner(ctx context.Context, conf ScannerConfig) (scanner.Scan // ImageRun runs scan on container image func ImageRun(ctx *cli.Context) error { - opt, err := initOption(ctx) - if err != nil { - return xerrors.Errorf("option error: %w", err) - } - - // Disable the lock file scanning - opt.DisabledAnalyzers = analyzer.TypeLockfiles - - if opt.Input != "" { - return archiveImageRun(ctx.Context, opt) - } - - return imageRun(ctx.Context, opt) -} - -func archiveImageRun(ctx context.Context, opt Option) error { - // standalone mode - scanner := archiveScanner - - if opt.RemoteAddr != "" { - // client/server mode - scanner = remoteArchiveScanner - } - - // scan tar file - return Run(ctx, opt, scanner, initCache) -} - -func imageRun(ctx context.Context, opt Option) error { - // standalone mode - scanner := imageScanner - - if opt.RemoteAddr != "" { - // client/server mode - scanner = remoteImageScanner - } - - // scan container image - return Run(ctx, opt, scanner, initCache) + return Run(ctx, containerImageArtifact) } diff --git a/pkg/commands/artifact/k8s.go b/pkg/commands/artifact/k8s.go index 7a22558521..04a5495de0 100644 --- a/pkg/commands/artifact/k8s.go +++ b/pkg/commands/artifact/k8s.go @@ -12,10 +12,6 @@ import ( "golang.org/x/xerrors" "gopkg.in/yaml.v2" - "github.com/aquasecurity/fanal/analyzer" - "github.com/aquasecurity/fanal/cache" - "github.com/aquasecurity/trivy-db/pkg/db" - "github.com/aquasecurity/trivy/pkg/log" pkgReport "github.com/aquasecurity/trivy/pkg/report" k8sReport "github.com/aquasecurity/trivy/pkg/report/k8s" @@ -27,35 +23,29 @@ import ( ) // K8sRun runs scan on kubernetes cluster -func K8sRun(ctx *cli.Context) error { - opt, err := initOption(ctx) +func K8sRun(cliCtx *cli.Context) error { + opt, err := InitOption(cliCtx) if err != nil { return xerrors.Errorf("option error: %w", err) } - if err = log.InitLogger(opt.Debug, true); err != nil { - return err - } + ctx, cancel := context.WithTimeout(cliCtx.Context, opt.Timeout) + defer cancel() - cacheClient, err := initCache(opt) + defer func() { + if xerrors.Is(err, context.DeadlineExceeded) { + log.Logger.Warn("Increase --timeout value") + } + }() + + runner, err := NewRunner(opt) if err != nil { - if errors.Is(err, errSkipScan) { + if errors.Is(err, SkipScan) { return nil } - return xerrors.Errorf("cache error: %w", err) - } - defer cacheClient.Close() - - // Disable DB update when using client/server - if opt.RemoteAddr == "" { - if err = initDB(opt); err != nil { - if errors.Is(err, errSkipScan) { - return nil - } - return xerrors.Errorf("DB error: %w", err) - } - defer db.Close() + return xerrors.Errorf("init error: %w", err) } + defer runner.Close() cluster, err := k8s.GetCluster() if err != nil { @@ -65,12 +55,12 @@ func K8sRun(ctx *cli.Context) error { trivyk8s := trivyk8s.New(cluster).Namespace(opt.KubernetesOption.Namespace) // list all kubernetes scannable artifacts - k8sArtifacts, err := trivyk8s.ListArtifacts(ctx.Context) + k8sArtifacts, err := trivyk8s.ListArtifacts(ctx) if err != nil { return xerrors.Errorf("get k8s artifacts error: %w", err) } - report, err := k8sRun(ctx, opt, cacheClient, k8sArtifacts) + report, err := k8sRun(ctx, runner, opt, k8sArtifacts) if err != nil { return xerrors.Errorf("k8s scan error: %w", err) } @@ -88,40 +78,34 @@ func K8sRun(ctx *cli.Context) error { return nil } -func k8sRun(cliContext *cli.Context, opt Option, cacheClient cache.Cache, k8sArtifacts []*artifacts.Artifact) (k8sReport.Report, error) { - ctx, cancel := context.WithTimeout(cliContext.Context, opt.Timeout) - defer cancel() +func k8sRun(ctx context.Context, runner *Runner, opt Option, artifacts []*artifacts.Artifact) (k8sReport.Report, error) { + opt.SecurityChecks = []string{types.SecurityCheckVulnerability, types.SecurityCheckConfig} // progress bar - bar := pb.StartNew(len(k8sArtifacts)) + bar := pb.StartNew(len(artifacts)) if opt.NoProgress { bar.SetWriter(io.Discard) } defer bar.Finish() - // image scanner configurations - imageScannerConfig, imageScannerOptions, err := initImageScannerConfig(ctx, opt, cacheClient) - if err != nil { - return k8sReport.Report{}, xerrors.Errorf("scanner config error: %w", err) - } - - // config scanner configurations - configScannerConfig, configScannerOptions, err := initConfigScannerConfig(ctx, opt, cacheClient) - if err != nil { - return k8sReport.Report{}, xerrors.Errorf("scanner config error: %w", err) - } - vulns := make([]k8sReport.Resource, 0) misconfigs := make([]k8sReport.Resource, 0) + // disable logs before scanning + err := log.InitLogger(opt.Debug, true) + if err != nil { + return k8sReport.Report{}, xerrors.Errorf("logger error: %w", err) + } + // Loops once over all artifacts, and execute scanners as necessary. Not every artifacts has an image, // so image scanner is not always executed. - for _, artifact := range k8sArtifacts { + for _, artifact := range artifacts { bar.Increment() // scan images if present for _, image := range artifact.Images { - imageReport, err := k8sScan(ctx, image, imageScanner, imageScannerConfig, imageScannerOptions) + opt.Target = image + imageReport, err := runner.ScanImage(ctx, opt) if err != nil { // add error to report log.Logger.Debugf("failed to scan image %s: %s", image, err) @@ -129,7 +113,7 @@ func k8sRun(cliContext *cli.Context, opt Option, cacheClient cache.Cache, k8sArt continue } - imageReport, err = filter(ctx, opt, imageReport) + imageReport, err = runner.Filter(ctx, opt, imageReport) if err != nil { return k8sReport.Report{}, xerrors.Errorf("filter error: %w", err) } @@ -138,14 +122,21 @@ func k8sRun(cliContext *cli.Context, opt Option, cacheClient cache.Cache, k8sArt } // scan configurations - configReport, err := k8sScanConfig(ctx, configScannerConfig, configScannerOptions, artifact) + configFile, err := createTempFile(artifact) + if err != nil { + return k8sReport.Report{}, xerrors.Errorf("scan error: %w", err) + } + + opt.Target = configFile + configReport, err := runner.ScanFilesystem(ctx, opt) + removeFile(configFile) if err != nil { // add error to report log.Logger.Debugf("failed to scan config %s/%s: %s", artifact.Kind, artifact.Name, err) misconfigs = append(misconfigs, newK8sResource(artifact, configReport, err)) } - configReport, err = filter(ctx, opt, configReport) + configReport, err = runner.Filter(ctx, opt, configReport) if err != nil { return k8sReport.Report{}, xerrors.Errorf("filter error: %w", err) } @@ -153,6 +144,12 @@ func k8sRun(cliContext *cli.Context, opt Option, cacheClient cache.Cache, k8sArt misconfigs = append(misconfigs, newK8sResource(artifact, configReport, nil)) } + // enable logs after scanning + err = log.InitLogger(opt.Debug, opt.Quiet) + if err != nil { + return k8sReport.Report{}, xerrors.Errorf("logger error: %w", err) + } + return k8sReport.Report{ SchemaVersion: 0, Vulnerabilities: vulns, @@ -160,58 +157,6 @@ func k8sRun(cliContext *cli.Context, opt Option, cacheClient cache.Cache, k8sArt }, nil } -func initImageScannerConfig(ctx context.Context, opt Option, cacheClient cache.Cache) (ScannerConfig, types.ScanOptions, error) { - // Disable the lock file scanning - opt.DisabledAnalyzers = analyzer.TypeLockfiles - - return initScannerConfig(ctx, opt, cacheClient) -} - -func initConfigScannerConfig(ctx context.Context, opt Option, cacheClient cache.Cache) (ScannerConfig, types.ScanOptions, error) { - // Disable OS and language analyzers - opt.DisabledAnalyzers = append(analyzer.TypeOSes, analyzer.TypeLanguages...) - - // Scan only config files - opt.VulnType = nil - opt.SecurityChecks = []string{types.SecurityCheckConfig} - - // Skip downloading vulnerability DB - opt.SkipDBUpdate = true - - return initScannerConfig(ctx, opt, cacheClient) -} - -func k8sScanConfig(ctx context.Context, config ScannerConfig, opts types.ScanOptions, a *artifacts.Artifact) (types.Report, error) { - fileName, err := createTempFile(a) - if err != nil { - return types.Report{}, xerrors.Errorf("scan error: %w", err) - } - defer removeFile(fileName) - - report, err := k8sScan(ctx, fileName, filesystemStandaloneScanner, config, opts) - if err != nil { - return types.Report{}, xerrors.Errorf("scan error: %w", err) - } - - return report, nil -} - -func k8sScan(ctx context.Context, target string, initializeScanner InitializeScanner, config ScannerConfig, opts types.ScanOptions) (types.Report, error) { - config.Target = target - s, cleanup, err := initializeScanner(ctx, config) - if err != nil { - log.Logger.Debugf("unexpected error during scanning %s: %s", config.Target, err) - return types.Report{}, err - } - defer cleanup() - - report, err := s.ScanArtifact(ctx, opts) - if err != nil { - return types.Report{}, xerrors.Errorf("artifact scan failed: %w", err) - } - return report, nil -} - func createTempFile(artifact *artifacts.Artifact) (string, error) { filename := fmt.Sprintf("%s-%s-%s-*.yaml", artifact.Namespace, artifact.Kind, artifact.Name) diff --git a/pkg/commands/artifact/repository.go b/pkg/commands/artifact/repository.go index 5305f4929a..de5d897f1c 100644 --- a/pkg/commands/artifact/repository.go +++ b/pkg/commands/artifact/repository.go @@ -6,13 +6,11 @@ import ( "github.com/urfave/cli/v2" "golang.org/x/xerrors" - "github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/scanner" - "github.com/aquasecurity/trivy/pkg/types" ) // filesystemStandaloneScanner initializes a repository scanner in standalone mode -func repositoryScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { +func repositoryStandaloneScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner, func(), error) { s, cleanup, err := initializeRepositoryScanner(ctx, conf.Target, conf.ArtifactCache, conf.LocalArtifactCache, conf.ArtifactOption) if err != nil { return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err) @@ -22,16 +20,5 @@ func repositoryScanner(ctx context.Context, conf ScannerConfig) (scanner.Scanner // RepositoryRun runs scan on repository func RepositoryRun(ctx *cli.Context) error { - opt, err := initOption(ctx) - if err != nil { - return xerrors.Errorf("option error: %w", err) - } - - // Do not scan OS packages - opt.VulnType = []string{types.VulnTypeLibrary} - - // Disable the OS analyzers and individual package analyzers - opt.DisabledAnalyzers = append(analyzer.TypeIndividualPkgs, analyzer.TypeOSes...) - - return Run(ctx.Context, opt, repositoryScanner, initCache) + return Run(ctx, repositoryArtifact) } diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 105d8b179d..dbee11ac2f 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -5,6 +5,7 @@ import ( "errors" "os" + "github.com/hashicorp/go-multierror" "github.com/urfave/cli/v2" "golang.org/x/exp/slices" "golang.org/x/xerrors" @@ -25,9 +26,27 @@ import ( "github.com/aquasecurity/trivy/pkg/utils" ) -var defaultPolicyNamespaces = []string{"appshield", "defsec", "builtin"} +type ArtifactType string -var errSkipScan = errors.New("skip subsequent processes") +const ( + containerImageArtifact ArtifactType = "image" + filesystemArtifact ArtifactType = "fs" + rootfsArtifact ArtifactType = "rootfs" + repositoryArtifact ArtifactType = "repo" + imageArchiveArtifact ArtifactType = "archive" +) + +var ( + defaultPolicyNamespaces = []string{"appshield", "defsec", "builtin"} + + supportedArtifactTypes = []ArtifactType{containerImageArtifact, filesystemArtifact, rootfsArtifact, + repositoryArtifact, imageArchiveArtifact} + + SkipScan = errors.New("skip subsequent processes") +) + +// InitializeScanner defines the initialize function signature of scanner +type InitializeScanner func(context.Context, ScannerConfig) (scanner.Scanner, func(), error) type ScannerConfig struct { // e.g. image name and file path @@ -44,60 +63,151 @@ type ScannerConfig struct { ArtifactOption artifact.Option } -// InitializeScanner defines the initialize function signature of scanner -type InitializeScanner func(context.Context, ScannerConfig) (scanner.Scanner, func(), error) - -// InitCache defines cache initializer -type InitCache func(c Option) (cache.Cache, error) - -// Run performs artifact scanning -func Run(ctx context.Context, opt Option, initializeScanner InitializeScanner, initCache InitCache) error { - ctx, cancel := context.WithTimeout(ctx, opt.Timeout) - defer cancel() - - err := runWithTimeout(ctx, opt, initializeScanner, initCache) - if xerrors.Is(err, context.DeadlineExceeded) { - log.Logger.Warn("Increase --timeout value") - } - return err +type Runner struct { + cache cache.Cache + dbOpen bool } -func runWithTimeout(ctx context.Context, opt Option, initializeScanner InitializeScanner, initCache InitCache) error { - if err := log.InitLogger(opt.Debug, opt.Quiet); err != nil { - return err +type runnerOption func(*Runner) + +// WithCacheClient takes a custom cache implementation +func WithCacheClient(c cache.Cache) runnerOption { + return func(r *Runner) { + r.cache = c + } +} + +// NewRunner initializes Runner that provides scanning functionalities. +// It is possible to return SkipScan and it must be handled by caller. +func NewRunner(cliOption Option, opts ...runnerOption) (*Runner, error) { + r := &Runner{} + for _, opt := range opts { + opt(r) } - cacheClient, err := initCache(opt) + err := log.InitLogger(cliOption.Debug, cliOption.Quiet) if err != nil { - if errors.Is(err, errSkipScan) { - return nil + return nil, xerrors.Errorf("logger error: %w", err) + } + + if err = r.initCache(cliOption); err != nil { + return nil, xerrors.Errorf("cache error: %w", err) + } + + if err = r.initDB(cliOption); err != nil { + return nil, xerrors.Errorf("DB error: %w", err) + } + + return r, nil +} + +// Close closes everything +func (r *Runner) Close() error { + var errs error + if err := r.cache.Close(); err != nil { + errs = multierror.Append(errs, err) + } + + if r.dbOpen { + if err := db.Close(); err != nil { + errs = multierror.Append(errs, err) } - return xerrors.Errorf("cache error: %w", err) } - defer cacheClient.Close() + return errs +} - // When scanning config files or running as client mode, it doesn't need to download the vulnerability database. - if opt.RemoteAddr == "" && slices.Contains(opt.SecurityChecks, types.SecurityCheckVulnerability) { - if err = initDB(opt); err != nil { - if errors.Is(err, errSkipScan) { - return nil - } - return xerrors.Errorf("DB error: %w", err) +func (r *Runner) ScanImage(ctx context.Context, opt Option) (types.Report, error) { + // Disable the lock file scanning + opt.DisabledAnalyzers = analyzer.TypeLockfiles + + var s InitializeScanner + switch { + case opt.Input != "" && opt.RemoteAddr == "": + // Scan image tarball in standalone mode + s = archiveStandaloneScanner + case opt.Input != "" && opt.RemoteAddr != "": + // Scan image tarball in client/server mode + s = archiveRemoteScanner + case opt.Input == "" && opt.RemoteAddr == "": + // Scan container image in standalone mode + s = imageStandaloneScanner + case opt.Input == "" && opt.RemoteAddr != "": + // Scan container image in client/server mode + s = imageRemoteScanner + } + + return r.Scan(ctx, opt, s) +} + +func (r *Runner) ScanFilesystem(ctx context.Context, opt Option) (types.Report, error) { + // Disable the individual package scanning + opt.DisabledAnalyzers = append(opt.DisabledAnalyzers, analyzer.TypeIndividualPkgs...) + + return r.scanFS(ctx, opt) +} + +func (r *Runner) ScanRootfs(ctx context.Context, opt Option) (types.Report, error) { + // Disable the lock file scanning + opt.DisabledAnalyzers = append(opt.DisabledAnalyzers, analyzer.TypeLockfiles...) + + return r.scanFS(ctx, opt) +} + +func (r *Runner) scanFS(ctx context.Context, opt Option) (types.Report, error) { + var s InitializeScanner + if opt.RemoteAddr == "" { + // Scan filesystem in standalone mode + s = filesystemStandaloneScanner + } else { + // Scan filesystem in client/server mode + s = filesystemRemoteScanner + } + + return r.Scan(ctx, opt, s) +} + +func (r *Runner) ScanRepository(ctx context.Context, opt Option) (types.Report, error) { + // Do not scan OS packages + opt.VulnType = []string{types.VulnTypeLibrary} + + // Disable the OS analyzers and individual package analyzers + opt.DisabledAnalyzers = append(analyzer.TypeIndividualPkgs, analyzer.TypeOSes...) + + return r.Scan(ctx, opt, repositoryStandaloneScanner) +} + +func (r *Runner) Scan(ctx context.Context, opt Option, initializeScanner InitializeScanner) (types.Report, error) { + report, err := scan(ctx, opt, initializeScanner, r.cache) + if err != nil { + return types.Report{}, xerrors.Errorf("scan error: %w", err) + } + + return report, nil +} + +func (r *Runner) Filter(ctx context.Context, opt Option, report types.Report) (types.Report, error) { + resultClient := initializeResultClient() + results := report.Results + for i := range results { + // Fill vulnerability info only in standalone mode + if opt.RemoteAddr == "" { + resultClient.FillVulnerabilityInfo(results[i].Vulnerabilities, results[i].Type) } - defer db.Close() + vulns, misconfSummary, misconfs, secrets, err := resultClient.Filter(ctx, results[i].Vulnerabilities, results[i].Misconfigurations, results[i].Secrets, + opt.Severities, opt.IgnoreUnfixed, opt.IncludeNonFailures, opt.IgnoreFile, opt.IgnorePolicy) + if err != nil { + return types.Report{}, xerrors.Errorf("unable to filter vulnerabilities: %w", err) + } + results[i].Vulnerabilities = vulns + results[i].Misconfigurations = misconfs + results[i].MisconfSummary = misconfSummary + results[i].Secrets = secrets } + return report, nil +} - report, err := scan(ctx, opt, initializeScanner, cacheClient) - if err != nil { - return xerrors.Errorf("scan error: %w", err) - } - - report, err = filter(ctx, opt, report) - if err != nil { - return xerrors.Errorf("filter error: %w", err) - } - - if err = pkgReport.Write(report, pkgReport.Option{ +func (r *Runner) Report(opt Option, report types.Report) error { + if err := pkgReport.Write(report, pkgReport.Option{ AppVersion: opt.GlobalOption.AppVersion, Format: opt.Format, Output: opt.Output, @@ -109,44 +219,15 @@ func runWithTimeout(ctx context.Context, opt Option, initializeScanner Initializ return xerrors.Errorf("unable to write results: %w", err) } - exit(opt, report.Results.Failed()) - return nil } -func initCache(c Option) (cache.Cache, error) { - // client/server mode - if c.RemoteAddr != "" { - remoteCache := tcache.NewRemoteCache(c.RemoteAddr, c.CustomHeaders, c.Insecure) - return tcache.NopCache(remoteCache), nil +func (r *Runner) initDB(c Option) error { + // When scanning config files or running as client mode, it doesn't need to download the vulnerability database. + if c.RemoteAddr != "" || !slices.Contains(c.SecurityChecks, types.SecurityCheckVulnerability) { + return nil } - // standalone mode - utils.SetCacheDir(c.CacheDir) - cache, err := operation.NewCache(c.CacheOption) - if err != nil { - return operation.Cache{}, xerrors.Errorf("unable to initialize the cache: %w", err) - } - log.Logger.Debugf("cache dir: %s", utils.CacheDir()) - - if c.Reset { - defer cache.Close() - if err = cache.Reset(); err != nil { - return operation.Cache{}, xerrors.Errorf("cache reset error: %w", err) - } - return operation.Cache{}, errSkipScan - } - if c.ClearCache { - defer cache.Close() - if err = cache.ClearArtifacts(); err != nil { - return operation.Cache{}, xerrors.Errorf("cache clear error: %w", err) - } - return operation.Cache{}, errSkipScan - } - return cache, nil -} - -func initDB(c Option) error { // download the database file noProgress := c.Quiet || c.NoProgress if err := operation.DownloadDB(c.AppVersion, c.CacheDir, c.DBRepository, noProgress, c.SkipDBUpdate); err != nil { @@ -154,16 +235,121 @@ func initDB(c Option) error { } if c.DownloadDBOnly { - return errSkipScan + return SkipScan } if err := db.Init(c.CacheDir); err != nil { return xerrors.Errorf("error in vulnerability DB initialize: %w", err) } + r.dbOpen = true + return nil } -func initOption(ctx *cli.Context) (Option, error) { +func (r *Runner) initCache(c Option) error { + // Skip initializing cache when custom cache is passed + if r.cache != nil { + return nil + } + + // client/server mode + if c.RemoteAddr != "" { + remoteCache := tcache.NewRemoteCache(c.RemoteAddr, c.CustomHeaders, c.Insecure) + r.cache = tcache.NopCache(remoteCache) + return nil + } + + // standalone mode + utils.SetCacheDir(c.CacheDir) + cache, err := operation.NewCache(c.CacheOption) + if err != nil { + return xerrors.Errorf("unable to initialize the cache: %w", err) + } + log.Logger.Debugf("cache dir: %s", utils.CacheDir()) + + if c.Reset { + defer cache.Close() + if err = cache.Reset(); err != nil { + return xerrors.Errorf("cache reset error: %w", err) + } + return SkipScan + } + if c.ClearCache { + defer cache.Close() + if err = cache.ClearArtifacts(); err != nil { + return xerrors.Errorf("cache clear error: %w", err) + } + return SkipScan + } + + r.cache = cache + return nil +} + +// Run performs artifact scanning +func Run(cliCtx *cli.Context, artifactType ArtifactType) error { + opt, err := InitOption(cliCtx) + if err != nil { + return err + } + + return run(cliCtx.Context, opt, artifactType) +} + +func run(ctx context.Context, opt Option, artifactType ArtifactType) (err error) { + ctx, cancel := context.WithTimeout(ctx, opt.Timeout) + defer cancel() + + defer func() { + if xerrors.Is(err, context.DeadlineExceeded) { + log.Logger.Warn("Increase --timeout value") + } + }() + + runner, err := NewRunner(opt) + if err != nil { + if errors.Is(err, SkipScan) { + return nil + } + return xerrors.Errorf("init error: %w", err) + } + defer runner.Close() + + var report types.Report + switch artifactType { + case containerImageArtifact, imageArchiveArtifact: + if report, err = runner.ScanImage(ctx, opt); err != nil { + return xerrors.Errorf("image scan error: %w", err) + } + case filesystemArtifact: + if report, err = runner.ScanFilesystem(ctx, opt); err != nil { + return xerrors.Errorf("filesystem scan error: %w", err) + } + case rootfsArtifact: + if report, err = runner.ScanRootfs(ctx, opt); err != nil { + return xerrors.Errorf("rootfs scan error: %w", err) + } + case repositoryArtifact: + if report, err = runner.ScanRepository(ctx, opt); err != nil { + return xerrors.Errorf("repository scan error: %w", err) + } + } + + report, err = runner.Filter(ctx, opt, report) + if err != nil { + return xerrors.Errorf("filter error: %w", err) + } + + if err = runner.Report(opt, report); err != nil { + return xerrors.Errorf("report error: %w", err) + } + + exit(opt, report.Results.Failed()) + + return nil +} + +func InitOption(ctx *cli.Context) (Option, error) { opt, err := NewOption(ctx) if err != nil { return Option{}, xerrors.Errorf("option error: %w", err) @@ -205,7 +391,7 @@ func disabledAnalyzers(opt Option) []analyzer.Type { return analyzers } -func initScannerConfig(ctx context.Context, opt Option, cacheClient cache.Cache) (ScannerConfig, types.ScanOptions, error) { +func initScannerConfig(opt Option, cacheClient cache.Cache) (ScannerConfig, types.ScanOptions, error) { target := opt.Target if opt.Input != "" { target = opt.Input @@ -262,7 +448,7 @@ func initScannerConfig(ctx context.Context, opt Option, cacheClient cache.Cache) func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner, cacheClient cache.Cache) ( types.Report, error) { - scannerConfig, scanOptions, err := initScannerConfig(ctx, opt, cacheClient) + scannerConfig, scanOptions, err := initScannerConfig(opt, cacheClient) if err != nil { return types.Report{}, err } @@ -280,27 +466,6 @@ func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner, return report, nil } -func filter(ctx context.Context, opt Option, report types.Report) (types.Report, error) { - resultClient := initializeResultClient() - results := report.Results - for i := range results { - // Fill vulnerability info only in standalone mode - if opt.RemoteAddr == "" { - resultClient.FillVulnerabilityInfo(results[i].Vulnerabilities, results[i].Type) - } - vulns, misconfSummary, misconfs, secrets, err := resultClient.Filter(ctx, results[i].Vulnerabilities, results[i].Misconfigurations, results[i].Secrets, - opt.Severities, opt.IgnoreUnfixed, opt.IncludeNonFailures, opt.IgnoreFile, opt.IgnorePolicy) - if err != nil { - return types.Report{}, xerrors.Errorf("unable to filter vulnerabilities: %w", err) - } - results[i].Vulnerabilities = vulns - results[i].Misconfigurations = misconfs - results[i].MisconfSummary = misconfSummary - results[i].Secrets = secrets - } - return report, nil -} - func exit(c Option, failedResults bool) { if c.ExitCode != 0 && failedResults { os.Exit(c.ExitCode) diff --git a/pkg/commands/artifact/sbom.go b/pkg/commands/artifact/sbom.go index 231555355e..2a4a2aeda3 100644 --- a/pkg/commands/artifact/sbom.go +++ b/pkg/commands/artifact/sbom.go @@ -2,61 +2,32 @@ package artifact import ( "github.com/urfave/cli/v2" - "golang.org/x/exp/maps" + "golang.org/x/exp/slices" "golang.org/x/xerrors" - "github.com/aquasecurity/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/types" ) -type ArtifactType string - -const ( - containerImageArtifact ArtifactType = "image" - filesystemArtifact ArtifactType = "fs" - repositoryArtifact ArtifactType = "repo" - imageArchiveArtifact ArtifactType = "archive" -) - -var artifactTypes = map[ArtifactType]struct { - initializer InitializeScanner - disableAnalyzers []analyzer.Type -}{ - containerImageArtifact: { - initializer: imageScanner, - disableAnalyzers: analyzer.TypeLockfiles, - }, - filesystemArtifact: { - initializer: filesystemStandaloneScanner, - disableAnalyzers: analyzer.TypeIndividualPkgs, - }, - repositoryArtifact: { - initializer: repositoryScanner, - disableAnalyzers: analyzer.TypeIndividualPkgs, - }, - imageArchiveArtifact: { - initializer: archiveScanner, - disableAnalyzers: analyzer.TypeLockfiles, - }, -} - // SbomRun runs generates sbom for image and package artifacts func SbomRun(ctx *cli.Context) error { - opt, err := initOption(ctx) + opt, err := InitOption(ctx) if err != nil { return xerrors.Errorf("option error: %w", err) } - artifactType := opt.SbomOption.ArtifactType - s, ok := artifactTypes[ArtifactType(artifactType)] - if !ok { - return xerrors.Errorf(`"--artifact-type" must be %q`, maps.Keys(artifactTypes)) + artifactType := ArtifactType(opt.SbomOption.ArtifactType) + if !slices.Contains(supportedArtifactTypes, artifactType) { + return xerrors.Errorf(`"--artifact-type" must be %q`, supportedArtifactTypes) + } + + // Pass the specified image archive via "--input". + if artifactType == imageArchiveArtifact { + opt.Input = opt.Target } // Scan the relevant dependencies - opt.DisabledAnalyzers = s.disableAnalyzers opt.ReportOption.VulnType = []string{types.VulnTypeOS, types.VulnTypeLibrary} opt.ReportOption.SecurityChecks = []string{types.SecurityCheckVulnerability} - return Run(ctx.Context, opt, s.initializer, initCache) + return run(ctx.Context, opt, artifactType) }