From 8d0ae1f5de72d92a043dcd6b7c164d30e51b6047 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Tue, 25 Jun 2024 13:06:27 +0400 Subject: [PATCH] feat!: add clean subcommand (#6993) Signed-off-by: knqyf263 Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Co-authored-by: DmitriyLewen --- .github/DISCUSSION_TEMPLATE/bugs.yml | 2 +- docs/docs/configuration/cache.md | 13 +- docs/docs/configuration/db.md | 6 +- .../references/configuration/cli/trivy.md | 1 + .../configuration/cli/trivy_clean.md | 50 +++++++ .../configuration/cli/trivy_config.md | 2 - .../configuration/cli/trivy_filesystem.md | 3 - .../configuration/cli/trivy_image.md | 3 - .../configuration/cli/trivy_kubernetes.md | 3 - .../configuration/cli/trivy_repository.md | 3 - .../configuration/cli/trivy_rootfs.md | 3 - .../configuration/cli/trivy_sbom.md | 2 - .../configuration/cli/trivy_server.md | 2 - .../references/configuration/cli/trivy_vm.md | 3 - docs/docs/references/troubleshooting.md | 4 +- docs/tutorials/integrations/gitlab-ci.md | 2 - mkdocs.yml | 1 + pkg/cache/client.go | 39 +---- pkg/cache/client_test.go | 50 ------- pkg/commands/app.go | 46 +++++- pkg/commands/artifact/run.go | 34 +---- pkg/commands/clean/run.go | 102 +++++++++++++ pkg/commands/clean/run_test.go | 135 ++++++++++++++++++ pkg/commands/server/run.go | 6 +- pkg/db/db.go | 8 ++ pkg/flag/cache_flags.go | 2 + pkg/flag/clean_flags.go | 84 +++++++++++ pkg/flag/db_flags.go | 10 +- pkg/flag/db_flags_test.go | 19 --- pkg/flag/misconf_flags.go | 2 + pkg/flag/options.go | 29 +++- pkg/flag/sbom_flags.go | 22 +-- pkg/flag/scan_flags.go | 2 +- pkg/javadb/client.go | 12 +- pkg/policy/policy.go | 1 - 35 files changed, 496 insertions(+), 210 deletions(-) create mode 100644 docs/docs/references/configuration/cli/trivy_clean.md create mode 100644 pkg/commands/clean/run.go create mode 100644 pkg/commands/clean/run_test.go create mode 100644 pkg/flag/clean_flags.go diff --git a/.github/DISCUSSION_TEMPLATE/bugs.yml b/.github/DISCUSSION_TEMPLATE/bugs.yml index 711b85c88334..59d0ee088365 100644 --- a/.github/DISCUSSION_TEMPLATE/bugs.yml +++ b/.github/DISCUSSION_TEMPLATE/bugs.yml @@ -116,7 +116,7 @@ body: label: Checklist description: Have you tried the following? options: - - label: Run `trivy image --reset` + - label: Run `trivy clean --all` - label: Read [the troubleshooting](https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/) - type: markdown attributes: diff --git a/docs/docs/configuration/cache.md b/docs/docs/configuration/cache.md index ff3a373c22ce..719b0deced17 100644 --- a/docs/docs/configuration/cache.md +++ b/docs/docs/configuration/cache.md @@ -9,24 +9,25 @@ The cache directory includes The cache option is common to all scanners. ## Clear Caches -The `--clear-cache` option removes caches. - -**The scan is not performed.** +`trivy clean` subcommand removes caches. ``` -$ trivy image --clear-cache +$ trivy clean --scan-cache ```
Result ``` -2019-11-15T15:13:26.209+0200 INFO Reopening vulnerability DB -2019-11-15T15:13:26.209+0200 INFO Removing image caches... +2024-06-21T21:58:21+04:00 INFO Removing scan cache... ```
+If you want to delete cached vulnerability databases, use `--vuln-db`. +You can also delete all caches with `--all`. +See `trivy clean --help` for details. + ## Cache Directory Specify where the cache is stored with `--cache-dir`. diff --git a/docs/docs/configuration/db.md b/docs/docs/configuration/db.md index 1479a79183a1..f6525fb61568 100644 --- a/docs/docs/configuration/db.md +++ b/docs/docs/configuration/db.md @@ -78,8 +78,10 @@ $ trivy image --java-db-repository registry.gitlab.com/gitlab-org/security-produ `java-db-registry:latest` => `java-db-registry:latest`, but `java-db-registry` => `java-db-registry:1`. ## Remove DBs -The `--reset` flag removes all caches and databases. +"trivy clean" command removes caches and databases. ``` -$ trivy image --reset +$ trivy clean --vuln-db --java-db +2024-06-24T11:42:31+06:00 INFO Removing vulnerability database... +2024-06-24T11:42:31+06:00 INFO Removing Java database... ``` \ No newline at end of file diff --git a/docs/docs/references/configuration/cli/trivy.md b/docs/docs/references/configuration/cli/trivy.md index 500a367fff8a..2992bc0faa9b 100644 --- a/docs/docs/references/configuration/cli/trivy.md +++ b/docs/docs/references/configuration/cli/trivy.md @@ -43,6 +43,7 @@ trivy [global flags] command [flags] target ### SEE ALSO +* [trivy clean](trivy_clean.md) - Remove cached files * [trivy config](trivy_config.md) - Scan config files for misconfigurations * [trivy convert](trivy_convert.md) - Convert Trivy JSON report into a different format * [trivy filesystem](trivy_filesystem.md) - Scan local filesystem diff --git a/docs/docs/references/configuration/cli/trivy_clean.md b/docs/docs/references/configuration/cli/trivy_clean.md new file mode 100644 index 000000000000..7a997bf7b581 --- /dev/null +++ b/docs/docs/references/configuration/cli/trivy_clean.md @@ -0,0 +1,50 @@ +## trivy clean + +Remove cached files + +``` +trivy clean [flags] +``` + +### Examples + +``` + # Remove all caches + $ trivy clean --all + + # Remove scan cache + $ trivy clean --scan-cache + + # Remove vulnerability database + $ trivy clean --vuln-db + +``` + +### Options + +``` + -a, --all remove all caches + --checks-bundle remove checks bundle + -h, --help help for clean + --java-db remove Java database + --scan-cache remove scan cache (container and VM image analysis results) + --vuln-db remove vulnerability database +``` + +### Options inherited from parent commands + +``` + --cache-dir string cache directory (default "/path/to/cache") + -c, --config string config path (default "trivy.yaml") + -d, --debug debug mode + --generate-default-config write the default config to trivy-default.yaml + --insecure allow insecure server connections + -q, --quiet suppress progress bar and log output + --timeout duration timeout (default 5m0s) + -v, --version show version +``` + +### SEE ALSO + +* [trivy](trivy.md) - Unified security scanner + diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 993570f1587b..f10788347129 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -14,7 +14,6 @@ trivy config [flags] DIR --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") - --clear-cache clear image caches without scanning --compliance string compliance report to generate --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded @@ -45,7 +44,6 @@ trivy config [flags] DIR --redis-tls enable redis TLS with public certificates, if using redis as cache backend --registry-token string registry token --report string specify a compliance report format for the output (all,summary) (default "all") - --reset-checks-bundle remove checks bundle -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL]) --skip-check-update skip fetching rego check updates --skip-dirs strings specify the directories or glob patterns to skip diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index 4d90f4e87e63..d024b2738d92 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -24,7 +24,6 @@ trivy filesystem [flags] PATH --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") - --clear-cache clear image caches without scanning --compliance string compliance report to generate --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded @@ -71,8 +70,6 @@ trivy filesystem [flags] PATH --registry-token string registry token --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --report string specify a compliance report format for the output (all,summary) (default "all") - --reset remove all caches and database - --reset-checks-bundle remove checks bundle --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,misconfig,secret,license) (default [vuln,secret]) --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index c61c6b648d7c..251ea85b6f33 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -38,7 +38,6 @@ trivy image [flags] IMAGE_NAME --cache-ttl duration cache TTL when using redis as cache backend --check-namespaces strings Rego namespaces --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") - --clear-cache clear image caches without scanning --compliance string compliance report to generate (docker-cis) --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded @@ -92,8 +91,6 @@ trivy image [flags] IMAGE_NAME --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --removed-pkgs detect vulnerabilities of removed packages (only for Alpine) --report string specify a format for the compliance report. (all,summary) (default "summary") - --reset remove all caches and database - --reset-checks-bundle remove checks bundle --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,misconfig,secret,license) (default [vuln,secret]) --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 54dc2db07f75..a39275750bde 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -34,7 +34,6 @@ trivy kubernetes [flags] [CONTEXT] --cache-ttl duration cache TTL when using redis as cache backend --check-namespaces strings Rego namespaces --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") - --clear-cache clear image caches without scanning --compliance string compliance report to generate (k8s-nsa,k8s-cis,k8s-pss-baseline,k8s-pss-restricted) --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded @@ -87,8 +86,6 @@ trivy kubernetes [flags] [CONTEXT] --registry-token string registry token --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --report string specify a report format for the output (all,summary) (default "all") - --reset remove all caches and database - --reset-checks-bundle remove checks bundle --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,misconfig,secret,rbac) (default [vuln,misconfig,secret,rbac]) --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 4f73c0d4e13d..963727554494 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -24,7 +24,6 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") - --clear-cache clear image caches without scanning --commit string pass the commit hash to be scanned --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded @@ -70,8 +69,6 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --redis-tls enable redis TLS with public certificates, if using redis as cache backend --registry-token string registry token --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") - --reset remove all caches and database - --reset-checks-bundle remove checks bundle --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,misconfig,secret,license) (default [vuln,secret]) --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 6f264b5c2362..ed287f689928 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -27,7 +27,6 @@ trivy rootfs [flags] ROOTDIR --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") - --clear-cache clear image caches without scanning --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded --custom-headers strings custom headers in client mode @@ -72,8 +71,6 @@ trivy rootfs [flags] ROOTDIR --redis-tls enable redis TLS with public certificates, if using redis as cache backend --registry-token string registry token --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") - --reset remove all caches and database - --reset-checks-bundle remove checks bundle --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,misconfig,secret,license) (default [vuln,secret]) --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index d5c4030d7281..b5681576c830 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -22,7 +22,6 @@ trivy sbom [flags] SBOM_PATH ``` --cache-backend string cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend - --clear-cache clear image caches without scanning --compliance string compliance report to generate --custom-headers strings custom headers in client mode --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") @@ -49,7 +48,6 @@ trivy sbom [flags] SBOM_PATH --redis-key string redis key file location, if using redis as cache backend --redis-tls enable redis TLS with public certificates, if using redis as cache backend --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") - --reset remove all caches and database --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln]) --server string server address in client mode diff --git a/docs/docs/references/configuration/cli/trivy_server.md b/docs/docs/references/configuration/cli/trivy_server.md index d888034c34bf..d8d711092e5e 100644 --- a/docs/docs/references/configuration/cli/trivy_server.md +++ b/docs/docs/references/configuration/cli/trivy_server.md @@ -22,7 +22,6 @@ trivy server [flags] ``` --cache-backend string cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend - --clear-cache clear image caches without scanning --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") --download-db-only download/update vulnerability database but don't run a scan --enable-modules strings [EXPERIMENTAL] module names to enable @@ -36,7 +35,6 @@ trivy server [flags] --redis-key string redis key file location, if using redis as cache backend --redis-tls enable redis TLS with public certificates, if using redis as cache backend --registry-token string registry token - --reset remove all caches and database --skip-db-update skip updating vulnerability database --token string for authentication in client/server mode --token-header string specify a header name for token in client/server mode (default "Trivy-Token") diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 51b7ad43cf38..67be823ed6b6 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -24,7 +24,6 @@ trivy vm [flags] VM_IMAGE --cache-backend string cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") - --clear-cache clear image caches without scanning --compliance string compliance report to generate --custom-headers strings custom headers in client mode --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") @@ -62,8 +61,6 @@ trivy vm [flags] VM_IMAGE --redis-key string redis key file location, if using redis as cache backend --redis-tls enable redis TLS with public certificates, if using redis as cache backend --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") - --reset remove all caches and database - --reset-checks-bundle remove checks bundle --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,misconfig,secret,license) (default [vuln,secret]) --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") diff --git a/docs/docs/references/troubleshooting.md b/docs/docs/references/troubleshooting.md index 7d2c3258aa2c..d271882c5ecb 100644 --- a/docs/docs/references/troubleshooting.md +++ b/docs/docs/references/troubleshooting.md @@ -264,10 +264,10 @@ $ brew install aquasecurity/trivy/trivy ## Others ### Unknown error -Try again with `--reset` option: +Try again after running `trivy clean --all`: ``` -$ trivy image --reset +$ trivy clean --all ``` [air-gapped]: ../advanced/air-gap.md diff --git a/docs/tutorials/integrations/gitlab-ci.md b/docs/tutorials/integrations/gitlab-ci.md index dbfe46d1ca4d..8b4e8c34e7bb 100644 --- a/docs/tutorials/integrations/gitlab-ci.md +++ b/docs/tutorials/integrations/gitlab-ci.md @@ -85,8 +85,6 @@ container_scanning: FULL_IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG script: - trivy --version - # cache cleanup is needed when scanning images with the same tags, it does not remove the database - - time trivy image --clear-cache # update vulnerabilities db - time trivy image --download-db-only # Builds report and puts it in the default workdir $CI_PROJECT_DIR, so `artifacts:` can take it from there diff --git a/mkdocs.yml b/mkdocs.yml index 042ffe789d6d..2222a30220fb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -152,6 +152,7 @@ nav: - Configuration: - CLI: - Overview: docs/references/configuration/cli/trivy.md + - Clean: docs/references/configuration/cli/trivy_clean.md - Config: docs/references/configuration/cli/trivy_config.md - Convert: docs/references/configuration/cli/trivy_convert.md - Filesystem: docs/references/configuration/cli/trivy_filesystem.md diff --git a/pkg/cache/client.go b/pkg/cache/client.go index 7b18827016d5..ab9dd4799428 100644 --- a/pkg/cache/client.go +++ b/pkg/cache/client.go @@ -20,11 +20,6 @@ const ( TypeRedis Type = "redis" ) -type Client struct { - dir string - Cache -} - type Type string type Options struct { @@ -115,9 +110,8 @@ func NewType(backend string) (Type, error) { } } -// NewClient returns a new cache client -func NewClient(dir string, opts Options) (*Client, error) { - client := &Client{dir: dir} +// New returns a new cache client +func New(dir string, opts Options) (Cache, error) { if opts.Type == TypeRedis { log.Info("Redis cache", log.String("url", opts.Redis.BackendMasked())) options, err := redis.ParseURL(opts.Redis.Backend) @@ -142,38 +136,15 @@ func NewClient(dir string, opts Options) (*Client, error) { } } - client.Cache = NewRedisCache(options, opts.TTL) - return client, nil + return NewRedisCache(options, opts.TTL), nil } // standalone mode - var err error - client.Cache, err = NewFSCache(dir) + fsCache, err := NewFSCache(dir) if err != nil { return nil, xerrors.Errorf("unable to initialize fs cache: %w", err) } - return client, nil -} - -// Reset resets the cache -func (c *Client) Reset() error { - log.Info("Removing all caches...") - if err := c.Clear(); err != nil { - return xerrors.Errorf("failed to remove the cache: %w", err) - } - if err := os.RemoveAll(c.dir); err != nil { - return xerrors.Errorf("failed to remove the directory (%s) : %w", c.dir, err) - } - return nil -} - -// ClearArtifacts clears the artifact cache -func (c *Client) ClearArtifacts() error { - log.Info("Removing artifact caches...") - if err := c.Clear(); err != nil { - return xerrors.Errorf("failed to remove the cache: %w", err) - } - return nil + return fsCache, nil } // GetTLSConfig gets tls config from CA, Cert and Key file diff --git a/pkg/cache/client_test.go b/pkg/cache/client_test.go index e41e84034a27..f22ce4f93e2f 100644 --- a/pkg/cache/client_test.go +++ b/pkg/cache/client_test.go @@ -1,8 +1,6 @@ package cache_test import ( - "os" - "path/filepath" "testing" "time" @@ -129,51 +127,3 @@ func TestRedisOptions_BackendMasked(t *testing.T) { }) } } - -func TestClient_Reset(t *testing.T) { - // Create a temporary directory for testing - tempDir := t.TempDir() - - // Create test files and subdirectories - subDir := filepath.Join(tempDir, "subdir") - err := os.MkdirAll(subDir, 0755) - require.NoError(t, err) - - testFile := filepath.Join(tempDir, "testfile.txt") - err = os.WriteFile(testFile, []byte("test content"), 0644) - require.NoError(t, err) - - // Create a cache client - client, err := cache.NewClient(tempDir, cache.Options{Type: cache.TypeFS}) - require.NoError(t, err) - - // Call Reset method - err = client.Reset() - require.NoError(t, err) - - // Verify that the subdirectory no longer exists - require.NoDirExists(t, subDir, "Subdirectory should not exist after Reset") - - // Verify that the test file no longer exists - require.NoFileExists(t, testFile, "Test file should not exist after Reset") - - // Verify that the cache directory no longer exists - require.NoDirExists(t, tempDir, "Cache directory should not exist after Reset") -} - -func TestClient_ClearArtifacts(t *testing.T) { - // Create a temporary directory for testing - tempDir := t.TempDir() - - // Create a client - client, err := cache.NewClient(tempDir, cache.Options{Type: cache.TypeFS}) - require.NoError(t, err) - - require.FileExists(t, filepath.Join(tempDir, "fanal", "fanal.db"), "Database file should exist") - - // Call ClearArtifacts method - err = client.ClearArtifacts() - require.NoError(t, err) - - require.NoDirExists(t, filepath.Join(tempDir, "fanal"), "Artifact cache should not exist after ClearArtifacts") -} diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 97ff44dc392a..0cd331e7b9b7 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -14,6 +14,7 @@ import ( "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/commands/artifact" + "github.com/aquasecurity/trivy/pkg/commands/clean" "github.com/aquasecurity/trivy/pkg/commands/convert" "github.com/aquasecurity/trivy/pkg/commands/server" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" @@ -94,6 +95,7 @@ func NewApp() *cobra.Command { NewSBOMCommand(globalFlags), NewVersionCommand(globalFlags), NewVMCommand(globalFlags), + NewCleanCommand(globalFlags), ) if plugins := loadPluginCommands(); len(plugins) > 0 { @@ -1160,6 +1162,47 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { return cmd } +func NewCleanCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { + cleanFlags := &flag.Flags{ + GlobalFlagGroup: globalFlags, + CleanFlagGroup: flag.NewCleanFlagGroup(), + } + cmd := &cobra.Command{ + Use: "clean [flags]", + GroupID: groupUtility, + Short: "Remove cached files", + Example: ` # Remove all caches + $ trivy clean --all + + # Remove scan cache + $ trivy clean --scan-cache + + # Remove vulnerability database + $ trivy clean --vuln-db +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := cleanFlags.Bind(cmd); err != nil { + return xerrors.Errorf("flag bind error: %w", err) + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + opts, err := cleanFlags.ToOptions(args) + if err != nil { + return xerrors.Errorf("flag error: %w", err) + } + + return clean.Run(cmd.Context(), opts) + }, + SilenceErrors: true, + SilenceUsage: true, + } + cmd.SetFlagErrorFunc(flagErrorFunc) + cleanFlags.AddFlags(cmd) + cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, cleanFlags.Usages(cmd))) + + return cmd +} func NewVersionCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { var versionFormat string cmd := &cobra.Command{ @@ -1199,7 +1242,8 @@ func showVersion(cacheDir, outputFormat string, w io.Writer) error { } func validateArgs(cmd *cobra.Command, args []string) error { - // '--clear-cache', '--download-db-only', '--download-java-db-only', '--reset', '--reset-checks-bundle' and '--generate-default-config' don't conduct the subsequent scanning + // '--clear-cache' (removed), '--download-db-only', '--download-java-db-only', '--reset' (removed), + // '--reset-checks-bundle' (removed) and '--generate-default-config' don't conduct the subsequent scanning if viper.GetBool(flag.ClearCacheFlag.ConfigName) || viper.GetBool(flag.DownloadDBOnlyFlag.ConfigName) || viper.GetBool(flag.ResetFlag.ConfigName) || viper.GetBool(flag.GenerateDefaultConfigFlag.ConfigName) || viper.GetBool(flag.DownloadJavaDBOnlyFlag.ConfigName) || viper.GetBool(flag.ResetChecksBundleFlag.ConfigName) { diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 65bfd455321b..bb2e144c5050 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -24,7 +24,6 @@ import ( "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/misconf" "github.com/aquasecurity/trivy/pkg/module" - "github.com/aquasecurity/trivy/pkg/policy" pkgReport "github.com/aquasecurity/trivy/pkg/report" "github.com/aquasecurity/trivy/pkg/result" "github.com/aquasecurity/trivy/pkg/rpc/client" @@ -350,41 +349,14 @@ func (r *runner) initCache(opts flag.Options) error { } // standalone mode - cacheClient, err := cache.NewClient(opts.CacheDir, opts.CacheOptions.CacheBackendOptions) + cacheClient, err := cache.New(opts.CacheDir, opts.CacheOptions.CacheBackendOptions) if err != nil { return xerrors.Errorf("unable to initialize the cache: %w", err) } log.Debug("Cache dir", log.String("dir", opts.CacheDir)) - if opts.Reset { - defer cacheClient.Close() - if err = cacheClient.Reset(); err != nil { - return xerrors.Errorf("cache reset error: %w", err) - } - return SkipScan - } - - if opts.ResetChecksBundle { - c, err := policy.NewClient(opts.CacheDir, true, opts.MisconfOptions.ChecksBundleRepository) - if err != nil { - return xerrors.Errorf("failed to instantiate check client: %w", err) - } - if err := c.Clear(); err != nil { - return xerrors.Errorf("failed to remove the cache: %w", err) - } - return SkipScan - } - - if opts.ClearCache { - defer cacheClient.Close() - if err = cacheClient.ClearArtifacts(); err != nil { - return xerrors.Errorf("cache clear error: %w", err) - } - return SkipScan - } - - r.cache = cacheClient.Cache - r.localCache = cacheClient.Cache + r.cache = cacheClient + r.localCache = cacheClient return nil } diff --git a/pkg/commands/clean/run.go b/pkg/commands/clean/run.go new file mode 100644 index 000000000000..f2ac57539d54 --- /dev/null +++ b/pkg/commands/clean/run.go @@ -0,0 +1,102 @@ +package clean + +import ( + "context" + "os" + + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/cache" + "github.com/aquasecurity/trivy/pkg/db" + "github.com/aquasecurity/trivy/pkg/flag" + "github.com/aquasecurity/trivy/pkg/javadb" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/policy" +) + +func Run(ctx context.Context, opts flag.Options) error { + ctx, cancel := context.WithTimeout(ctx, opts.Timeout) + defer cancel() + + if !opts.CleanAll && !opts.CleanScanCache && !opts.CleanVulnerabilityDB && !opts.CleanJavaDB && !opts.CleanChecksBundle { + return xerrors.New("no clean option is specified") + } + + if opts.CleanAll { + return cleanAll(ctx, opts) + } + + if opts.CleanScanCache { + if err := cleanScanCache(ctx, opts); err != nil { + return xerrors.Errorf("failed to remove scan cache : %w", err) + } + } + + if opts.CleanVulnerabilityDB { + if err := cleanVulnerabilityDB(ctx, opts); err != nil { + return xerrors.Errorf("vuln db clean error: %w", err) + } + } + + if opts.CleanJavaDB { + if err := cleanJavaDB(ctx, opts); err != nil { + return xerrors.Errorf("java db clean error: %w", err) + } + } + + if opts.CleanChecksBundle { + if err := cleanCheckBundle(opts); err != nil { + return xerrors.Errorf("check bundle clean error: %w", err) + } + } + return nil +} + +func cleanAll(ctx context.Context, opts flag.Options) error { + log.InfoContext(ctx, "Removing all caches...") + if err := os.RemoveAll(opts.CacheDir); err != nil { + return xerrors.Errorf("failed to remove the directory (%s) : %w", opts.CacheDir, err) + } + return nil +} + +func cleanScanCache(ctx context.Context, opts flag.Options) error { + log.InfoContext(ctx, "Removing scan cache...") + c, err := cache.New(opts.CacheDir, opts.CacheBackendOptions) + if err != nil { + return xerrors.Errorf("failed to instantiate cache client: %w", err) + } + if err = c.Clear(); err != nil { + return xerrors.Errorf("clear scan cache: %w", err) + } + return nil +} + +func cleanVulnerabilityDB(ctx context.Context, opts flag.Options) error { + log.InfoContext(ctx, "Removing vulnerability database...") + if err := db.NewClient(opts.CacheDir, true).Clear(ctx); err != nil { + return xerrors.Errorf("clear vulnerability database: %w", err) + + } + return nil +} + +func cleanJavaDB(ctx context.Context, opts flag.Options) error { + log.InfoContext(ctx, "Removing Java database...") + if err := javadb.Clear(ctx, opts.CacheDir); err != nil { + return xerrors.Errorf("clear Java database: %w", err) + } + return nil +} + +func cleanCheckBundle(opts flag.Options) error { + log.Info("Removing check bundle...") + c, err := policy.NewClient(opts.CacheDir, true, opts.MisconfOptions.ChecksBundleRepository) + if err != nil { + return xerrors.Errorf("failed to instantiate check client: %w", err) + } + if err := c.Clear(); err != nil { + return xerrors.Errorf("clear check bundle: %w", err) + } + return nil +} diff --git a/pkg/commands/clean/run_test.go b/pkg/commands/clean/run_test.go new file mode 100644 index 000000000000..a26fef86a572 --- /dev/null +++ b/pkg/commands/clean/run_test.go @@ -0,0 +1,135 @@ +package clean_test + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/commands/clean" + "github.com/aquasecurity/trivy/pkg/flag" +) + +func TestRun(t *testing.T) { + tests := []struct { + name string + cleanOpts flag.CleanOptions + wantErr bool + checkFunc func(*testing.T, string) + }{ + { + name: "clean all", + cleanOpts: flag.CleanOptions{ + CleanAll: true, + }, + wantErr: false, + checkFunc: func(t *testing.T, dir string) { + assert.NoDirExists(t, dir) + }, + }, + { + name: "clean scan cache", + cleanOpts: flag.CleanOptions{ + CleanScanCache: true, + }, + wantErr: false, + checkFunc: func(t *testing.T, dir string) { + assert.NoDirExists(t, filepath.Join(dir, "fanal")) + assert.DirExists(t, filepath.Join(dir, "db")) + assert.DirExists(t, filepath.Join(dir, "java-db")) + assert.DirExists(t, filepath.Join(dir, "policy")) + }, + }, + { + name: "clean vulnerability DB", + cleanOpts: flag.CleanOptions{ + CleanVulnerabilityDB: true, + }, + wantErr: false, + checkFunc: func(t *testing.T, dir string) { + assert.NoDirExists(t, filepath.Join(dir, "db")) + assert.DirExists(t, filepath.Join(dir, "fanal")) + assert.DirExists(t, filepath.Join(dir, "java-db")) + assert.DirExists(t, filepath.Join(dir, "policy")) + }, + }, + { + name: "clean Java DB", + cleanOpts: flag.CleanOptions{ + CleanJavaDB: true, + }, + wantErr: false, + checkFunc: func(t *testing.T, dir string) { + assert.NoDirExists(t, filepath.Join(dir, "java-db")) + assert.DirExists(t, filepath.Join(dir, "fanal")) + assert.DirExists(t, filepath.Join(dir, "db")) + assert.DirExists(t, filepath.Join(dir, "policy")) + }, + }, + { + name: "clean check bundle", + cleanOpts: flag.CleanOptions{ + CleanChecksBundle: true, + }, + wantErr: false, + checkFunc: func(t *testing.T, dir string) { + assert.NoDirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "fanal")) + assert.DirExists(t, filepath.Join(dir, "db")) + assert.DirExists(t, filepath.Join(dir, "java-db")) + }, + }, + { + name: "no clean option specified", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a temporary directory for testing + tempDir := t.TempDir() + + // Create test directories and files + createTestFiles(t, tempDir) + + opts := flag.Options{ + GlobalOptions: flag.GlobalOptions{ + CacheDir: tempDir, + }, + CleanOptions: tt.cleanOpts, + } + + err := clean.Run(context.Background(), opts) + + if tt.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + if tt.checkFunc != nil { + tt.checkFunc(t, tempDir) + } + }) + } +} + +func createTestFiles(t *testing.T, dir string) { + subdirs := []string{ + "fanal", + "db", + "java-db", + "policy", + } + for _, subdir := range subdirs { + err := os.MkdirAll(filepath.Join(dir, subdir), 0755) + require.NoError(t, err) + + testFile := filepath.Join(dir, subdir, "testfile.txt") + err = os.WriteFile(testFile, []byte("test content"), 0644) + require.NoError(t, err) + } +} diff --git a/pkg/commands/server/run.go b/pkg/commands/server/run.go index f2957b75dbb6..917f1b4aa459 100644 --- a/pkg/commands/server/run.go +++ b/pkg/commands/server/run.go @@ -19,17 +19,13 @@ func Run(ctx context.Context, opts flag.Options) (err error) { log.InitLogger(opts.Debug, opts.Quiet) // configure cache dir - cacheClient, err := cache.NewClient(opts.CacheDir, opts.CacheOptions.CacheBackendOptions) + cacheClient, err := cache.New(opts.CacheDir, opts.CacheOptions.CacheBackendOptions) if err != nil { return xerrors.Errorf("server cache error: %w", err) } defer cacheClient.Close() log.Debug("Cache", log.String("dir", opts.CacheDir)) - if opts.Reset { - return cacheClient.Reset() - } - // download the database file if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, true, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil { diff --git a/pkg/db/db.go b/pkg/db/db.go index a006404d2c83..e87277f93375 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "os" "time" "github.com/google/go-containerregistry/pkg/name" @@ -158,6 +159,13 @@ func (c *Client) Download(ctx context.Context, dst string, opt types.RegistryOpt return nil } +func (c *Client) Clear(ctx context.Context) error { + if err := os.RemoveAll(db.Dir(c.cacheDir)); err != nil { + return xerrors.Errorf("failed to remove vulnerability database: %w", err) + } + return nil +} + func (c *Client) updateDownloadedAt(ctx context.Context, dst string) error { log.Debug("Updating database metadata...") diff --git a/pkg/flag/cache_flags.go b/pkg/flag/cache_flags.go index 75b7a1837371..73a31fd2684d 100644 --- a/pkg/flag/cache_flags.go +++ b/pkg/flag/cache_flags.go @@ -18,10 +18,12 @@ import ( // cert: cert.pem // key: key.pem var ( + // Deprecated ClearCacheFlag = Flag[bool]{ Name: "clear-cache", ConfigName: "cache.clear", Usage: "clear image caches without scanning", + Removed: `Use "trivy clean --scan-cache" instead`, } CacheBackendFlag = Flag[string]{ Name: "cache-backend", diff --git a/pkg/flag/clean_flags.go b/pkg/flag/clean_flags.go new file mode 100644 index 000000000000..7a898c38ad63 --- /dev/null +++ b/pkg/flag/clean_flags.go @@ -0,0 +1,84 @@ +package flag + +var ( + CleanAll = Flag[bool]{ + Name: "all", + Shorthand: "a", + ConfigName: "clean.all", + Usage: "remove all caches", + } + CleanScanCache = Flag[bool]{ + Name: "scan-cache", + ConfigName: "clean.scan-cache", + Usage: "remove scan cache (container and VM image analysis results)", + } + CleanVulnerabilityDB = Flag[bool]{ + Name: "vuln-db", + ConfigName: "clean.vuln-db", + Usage: "remove vulnerability database", + } + CleanJavaDB = Flag[bool]{ + Name: "java-db", + ConfigName: "clean.java-db", + Usage: "remove Java database", + } + CleanChecksBundle = Flag[bool]{ + Name: "checks-bundle", + ConfigName: "clean.checks-bundle", + Usage: "remove checks bundle", + } +) + +type CleanFlagGroup struct { + CleanAll *Flag[bool] + CleanVulnerabilityDB *Flag[bool] + CleanJavaDB *Flag[bool] + CleanChecksBundle *Flag[bool] + CleanScanCache *Flag[bool] +} + +type CleanOptions struct { + CleanAll bool + CleanVulnerabilityDB bool + CleanJavaDB bool + CleanChecksBundle bool + CleanScanCache bool +} + +func NewCleanFlagGroup() *CleanFlagGroup { + return &CleanFlagGroup{ + CleanAll: CleanAll.Clone(), + CleanVulnerabilityDB: CleanVulnerabilityDB.Clone(), + CleanJavaDB: CleanJavaDB.Clone(), + CleanChecksBundle: CleanChecksBundle.Clone(), + CleanScanCache: CleanScanCache.Clone(), + } +} + +func (fg *CleanFlagGroup) Name() string { + return "Clean" +} + +func (fg *CleanFlagGroup) Flags() []Flagger { + return []Flagger{ + fg.CleanAll, + fg.CleanVulnerabilityDB, + fg.CleanJavaDB, + fg.CleanChecksBundle, + fg.CleanScanCache, + } +} + +func (fg *CleanFlagGroup) ToOptions() (CleanOptions, error) { + if err := parseFlags(fg); err != nil { + return CleanOptions{}, err + } + + return CleanOptions{ + CleanAll: fg.CleanAll.Value(), + CleanVulnerabilityDB: fg.CleanVulnerabilityDB.Value(), + CleanJavaDB: fg.CleanJavaDB.Value(), + CleanChecksBundle: fg.CleanChecksBundle.Value(), + CleanScanCache: fg.CleanScanCache.Value(), + }, nil +} diff --git a/pkg/flag/db_flags.go b/pkg/flag/db_flags.go index fd426ae9ccbb..37685b104204 100644 --- a/pkg/flag/db_flags.go +++ b/pkg/flag/db_flags.go @@ -12,10 +12,12 @@ import ( ) var ( + // Deprecated ResetFlag = Flag[bool]{ Name: "reset", ConfigName: "reset", Usage: "remove all caches and database", + Removed: `Use "trivy clean --all" instead.`, } DownloadDBOnlyFlag = Flag[bool]{ Name: "download-db-only", @@ -64,7 +66,7 @@ var ( Name: "light", ConfigName: "db.light", Usage: "deprecated", - Deprecated: true, + Deprecated: `This flag is ignored.`, } ) @@ -90,7 +92,6 @@ type DBOptions struct { NoProgress bool DBRepository name.Reference JavaDBRepository name.Reference - Light bool // deprecated } // NewDBFlagGroup returns a default DBFlagGroup @@ -135,7 +136,6 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) { skipJavaDBUpdate := f.SkipJavaDBUpdate.Value() downloadDBOnly := f.DownloadDBOnly.Value() downloadJavaDBOnly := f.DownloadJavaDBOnly.Value() - light := f.Light.Value() if downloadDBOnly && skipDBUpdate { return DBOptions{}, xerrors.New("--skip-db-update and --download-db-only options can not be specified both") @@ -143,9 +143,6 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) { if downloadJavaDBOnly && skipJavaDBUpdate { return DBOptions{}, xerrors.New("--skip-java-db-update and --download-java-db-only options can not be specified both") } - if light { - log.Warn("'--light' option is deprecated and will be removed. See also: https://github.com/aquasecurity/trivy/discussions/1649") - } var dbRepository, javaDBRepository name.Reference var err error @@ -179,7 +176,6 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) { SkipDBUpdate: skipDBUpdate, DownloadJavaDBOnly: downloadJavaDBOnly, SkipJavaDBUpdate: skipJavaDBUpdate, - Light: light, NoProgress: f.NoProgress.Value(), DBRepository: dbRepository, JavaDBRepository: javaDBRepository, diff --git a/pkg/flag/db_flags_test.go b/pkg/flag/db_flags_test.go index fb6b31effaf4..2bbd5b00198c 100644 --- a/pkg/flag/db_flags_test.go +++ b/pkg/flag/db_flags_test.go @@ -43,23 +43,6 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { }, assertion: require.NoError, }, - { - name: "light", - fields: fields{ - Light: true, - DBRepository: "ghcr.io/aquasecurity/trivy-db", - JavaDBRepository: "ghcr.io/aquasecurity/trivy-java-db", - }, - want: flag.DBOptions{ - Light: true, - DBRepository: name.Tag{}, // All fields are unexported - JavaDBRepository: name.Tag{}, // All fields are unexported - }, - wantLogs: []string{ - "'--light' option is deprecated and will be removed. See also: https://github.com/aquasecurity/trivy/discussions/1649", - }, - assertion: require.NoError, - }, { name: "sad", fields: fields{ @@ -88,7 +71,6 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { viper.Set(flag.SkipDBUpdateFlag.ConfigName, tt.fields.SkipDBUpdate) viper.Set(flag.DownloadDBOnlyFlag.ConfigName, tt.fields.DownloadDBOnly) - viper.Set(flag.LightFlag.ConfigName, tt.fields.Light) viper.Set(flag.DBRepositoryFlag.ConfigName, tt.fields.DBRepository) viper.Set(flag.JavaDBRepositoryFlag.ConfigName, tt.fields.JavaDBRepository) @@ -96,7 +78,6 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { f := &flag.DBFlagGroup{ DownloadDBOnly: flag.DownloadDBOnlyFlag.Clone(), SkipDBUpdate: flag.SkipDBUpdateFlag.Clone(), - Light: flag.LightFlag.Clone(), DBRepository: flag.DBRepositoryFlag.Clone(), JavaDBRepository: flag.JavaDBRepositoryFlag.Clone(), } diff --git a/pkg/flag/misconf_flags.go b/pkg/flag/misconf_flags.go index a7f929fc4590..fc7505fec393 100644 --- a/pkg/flag/misconf_flags.go +++ b/pkg/flag/misconf_flags.go @@ -15,10 +15,12 @@ import ( // config-policy: "custom-policy/policy" // policy-namespaces: "user" var ( + // Deprecated ResetChecksBundleFlag = Flag[bool]{ Name: "reset-checks-bundle", ConfigName: "misconfiguration.reset-checks-bundle", Usage: "remove checks bundle", + Removed: `Use "trivy clean --checks-bundle" instead`, Aliases: []Alias{ { Name: "reset-policy-bundle", diff --git a/pkg/flag/options.go b/pkg/flag/options.go index db042ff802a1..ee18c45c4092 100644 --- a/pkg/flag/options.go +++ b/pkg/flag/options.go @@ -59,7 +59,10 @@ type Flag[T FlagType] struct { Persistent bool // Deprecated represents if the flag is deprecated - Deprecated bool + Deprecated string + + // Removed represents if the flag is removed and no longer works + Removed string // Aliases represents aliases Aliases []Alias @@ -107,6 +110,14 @@ func (f *Flag[T]) Parse() error { return xerrors.Errorf(`invalid argument "%s" for "--%s" flag: must be one of %q`, value, f.Name, f.Values) } + if f.Deprecated != "" && f.isSet() { + log.Warnf(`"--%s" is deprecated. %s`, f.Name, f.Deprecated) + } + if f.Removed != "" && f.isSet() { + log.Errorf(`"--%s" was removed. %s`, f.Name, f.Removed) + return xerrors.Errorf(`removed flag ("--%s")`, f.Name) + } + f.value = value return nil } @@ -229,8 +240,8 @@ func (f *Flag[T]) Add(cmd *cobra.Command) { flags.Float64P(f.Name, f.Shorthand, v, f.Usage) } - if f.Deprecated { - flags.MarkHidden(f.Name) // nolint: gosec + if f.Deprecated != "" || f.Removed != "" { + _ = flags.MarkHidden(f.Name) } } @@ -301,6 +312,7 @@ type Flags struct { GlobalFlagGroup *GlobalFlagGroup AWSFlagGroup *AWSFlagGroup CacheFlagGroup *CacheFlagGroup + CleanFlagGroup *CleanFlagGroup DBFlagGroup *DBFlagGroup ImageFlagGroup *ImageFlagGroup K8sFlagGroup *K8sFlagGroup @@ -323,6 +335,7 @@ type Options struct { GlobalOptions AWSOptions CacheOptions + CleanOptions DBOptions ImageOptions K8sOptions @@ -495,6 +508,9 @@ func (f *Flags) groups() []FlagGroup { if f.CacheFlagGroup != nil { groups = append(groups, f.CacheFlagGroup) } + if f.CleanFlagGroup != nil { + groups = append(groups, f.CleanFlagGroup) + } if f.DBFlagGroup != nil { groups = append(groups, f.DBFlagGroup) } @@ -621,6 +637,13 @@ func (f *Flags) ToOptions(args []string) (Options, error) { } } + if f.CleanFlagGroup != nil { + opts.CleanOptions, err = f.CleanFlagGroup.ToOptions() + if err != nil { + return Options{}, xerrors.Errorf("clean flag error: %w", err) + } + } + if f.DBFlagGroup != nil { opts.DBOptions, err = f.DBFlagGroup.ToOptions() if err != nil { diff --git a/pkg/flag/sbom_flags.go b/pkg/flag/sbom_flags.go index 388911abd83e..9af8414ed546 100644 --- a/pkg/flag/sbom_flags.go +++ b/pkg/flag/sbom_flags.go @@ -1,23 +1,17 @@ package flag -import ( - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/log" -) - var ( ArtifactTypeFlag = Flag[string]{ Name: "artifact-type", ConfigName: "sbom.artifact-type", Usage: "deprecated", - Deprecated: true, + Removed: `Use 'trivy image' or other subcommands. See also https://github.com/aquasecurity/trivy/discussions/2407`, } SBOMFormatFlag = Flag[string]{ Name: "sbom-format", ConfigName: "sbom.format", Usage: "deprecated", - Deprecated: true, + Removed: `Use 'trivy image' or other subcommands. See also https://github.com/aquasecurity/trivy/discussions/2407`, } ) @@ -26,8 +20,7 @@ type SBOMFlagGroup struct { SBOMFormat *Flag[string] // deprecated } -type SBOMOptions struct { -} +type SBOMOptions struct{} func NewSBOMFlagGroup() *SBOMFlagGroup { return &SBOMFlagGroup{ @@ -52,14 +45,5 @@ func (f *SBOMFlagGroup) ToOptions() (SBOMOptions, error) { return SBOMOptions{}, err } - artifactType := f.ArtifactType.Value() - sbomFormat := f.SBOMFormat.Value() - - if artifactType != "" || sbomFormat != "" { - log.Error("'trivy sbom' is now for scanning SBOM. " + - "See https://github.com/aquasecurity/trivy/discussions/2407 for the detail") - return SBOMOptions{}, xerrors.New("'--artifact-type' and '--sbom-format' are no longer available") - } - return SBOMOptions{}, nil } diff --git a/pkg/flag/scan_flags.go b/pkg/flag/scan_flags.go index 102e16e2fdd4..07f14a08551a 100644 --- a/pkg/flag/scan_flags.go +++ b/pkg/flag/scan_flags.go @@ -73,7 +73,7 @@ var ( ConfigName: "scan.slow", Default: false, Usage: "scan over time with lower CPU and memory utilization", - Deprecated: true, + Deprecated: `Use "--parallel 1" instead.`, } ParallelFlag = Flag[int]{ Name: "parallel", diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index 408456e16500..ca055a0c333f 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -79,7 +79,7 @@ func (u *Updater) Update() error { return xerrors.Errorf("Java DB metadata update error: %w", err) } log.Info("The Java DB is cached for 3 days. If you want to update the database more frequently, " + - "the '--reset' flag clears the DB cache.") + `"trivy clean --java-db" command clears the DB cache.`) } return nil @@ -88,7 +88,7 @@ func (u *Updater) Update() error { func Init(cacheDir string, javaDBRepository name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) { updater = &Updater{ repo: javaDBRepository, - dbDir: filepath.Join(cacheDir, "java-db"), + dbDir: dbDir(cacheDir), skip: skip, quiet: quiet, registryOption: registryOption, @@ -107,6 +107,14 @@ func Update() error { return err } +func Clear(ctx context.Context, cacheDir string) error { + return os.RemoveAll(dbDir(cacheDir)) +} + +func dbDir(cacheDir string) string { + return filepath.Join(cacheDir, "java-db") +} + type DB struct { driver db.DB } diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index 950644c0084d..88af220d3cf6 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -237,7 +237,6 @@ func (c *Client) GetMetadata() (*Metadata, error) { } func (c *Client) Clear() error { - log.Info("Removing check bundle...") if err := os.RemoveAll(c.policyDir); err != nil { return xerrors.Errorf("failed to remove check bundle: %w", err) }