refactor: add allowed values for CLI flags (#4800)

* refactor: rename Value to Default

* refactor: support allowed values for CLI flags

* docs: auto-generate

* test: fix

* test: add tests for flags
This commit is contained in:
Teppei Fukuda
2023-07-17 16:13:23 +03:00
committed by GitHub
parent 4cecd17ea5
commit aca11b95d0
38 changed files with 468 additions and 294 deletions

View File

@@ -87,10 +87,10 @@ trivy aws [flags]
-o, --output string output file name -o, --output string output file name
--policy-namespaces strings Rego namespaces --policy-namespaces strings Rego namespaces
--region string AWS Region to scan --region string AWS Region to scan
--report string specify a report format for the output. (all,summary) (default "all") --report string specify a report format for the output (all,summary) (default "all")
--reset-policy-bundle remove policy bundle --reset-policy-bundle remove policy bundle
--service strings Only scan AWS Service(s) specified with this flag. Can specify multiple services using --service A --service B etc. --service strings Only scan AWS Service(s) specified with this flag. Can specify multiple services using --service A --service B etc.
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-policy-update skip fetching rego policy updates --skip-policy-update skip fetching rego policy updates
--skip-service strings Skip selected AWS Service(s) specified with this flag. Can specify multiple services using --skip-service A --skip-service B etc. --skip-service strings Skip selected AWS Service(s) specified with this flag. Can specify multiple services using --skip-service A --skip-service B etc.
-t, --template string output template -t, --template string output template

View File

@@ -36,9 +36,9 @@ trivy config [flags] DIR
--redis-key string redis key file location, if using redis as cache backend --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 --redis-tls enable redis TLS with public certificates, if using redis as cache backend
--registry-token string registry token --registry-token string registry token
--report string specify a compliance report format for the output. (all,summary) (default "all") --report string specify a compliance report format for the output (all,summary) (default "all")
--reset-policy-bundle remove policy bundle --reset-policy-bundle remove policy bundle
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal
--skip-policy-update skip fetching rego policy updates --skip-policy-update skip fetching rego policy updates

View File

@@ -28,8 +28,8 @@ trivy convert [flags] RESULT_JSON
--ignorefile string specify .trivyignore file (default ".trivyignore") --ignorefile string specify .trivyignore file (default ".trivyignore")
--list-all-pkgs enabling the option will output all packages regardless of vulnerability --list-all-pkgs enabling the option will output all packages regardless of vulnerability
-o, --output string output file name -o, --output string output file name
--report string specify a report format for the output. (all,summary) (default "all") --report string specify a report format for the output (all,summary) (default "all")
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
-t, --template string output template -t, --template string output template
``` ```

View File

@@ -61,14 +61,14 @@ trivy filesystem [flags] PATH
--redis-tls enable redis TLS with public certificates, 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 --registry-token string registry token
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --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") --report string specify a compliance report format for the output (all,summary) (default "all")
--reset remove all caches and database --reset remove all caches and database
--reset-policy-bundle remove policy bundle --reset-policy-bundle remove policy bundle
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --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,config,secret,license) (default [vuln,secret]) --scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret])
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode --server string server address in client mode
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-db-update skip updating vulnerability database --skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal

View File

@@ -60,7 +60,7 @@ trivy image [flags] IMAGE_NAME
--ignore-unfixed display only fixed vulnerabilities --ignore-unfixed display only fixed vulnerabilities
--ignored-licenses strings specify a list of license to ignore --ignored-licenses strings specify a list of license to ignore
--ignorefile string specify .trivyignore file (default ".trivyignore") --ignorefile string specify .trivyignore file (default ".trivyignore")
--image-config-scanners string comma-separated list of what security issues to detect on container image configurations (config,secret) --image-config-scanners strings comma-separated list of what security issues to detect on container image configurations (config,secret)
--image-src strings image source(s) to use, in priority order (docker,containerd,podman,remote) (default [docker,containerd,podman,remote]) --image-src strings image source(s) to use, in priority order (docker,containerd,podman,remote) (default [docker,containerd,podman,remote])
--include-non-failures include successes and exceptions, available with '--scanners config' --include-non-failures include successes and exceptions, available with '--scanners config'
--input string input file path instead of image name --input string input file path instead of image name
@@ -82,14 +82,14 @@ trivy image [flags] IMAGE_NAME
--registry-token string registry token --registry-token string registry token
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--removed-pkgs detect vulnerabilities of removed packages (only for Alpine) --removed-pkgs detect vulnerabilities of removed packages (only for Alpine)
--report string specify a format for the compliance report. (default "summary") --report string specify a format for the compliance report. (all,summary) (default "summary")
--reset remove all caches and database --reset remove all caches and database
--reset-policy-bundle remove policy bundle --reset-policy-bundle remove policy bundle
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --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,config,secret,license) (default [vuln,secret]) --scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret])
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode --server string server address in client mode
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-db-update skip updating vulnerability database --skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal

View File

@@ -32,7 +32,7 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg:
--cache-ttl duration cache TTL when using redis as cache backend --cache-ttl duration cache TTL when using redis as cache backend
--clear-cache clear image caches without scanning --clear-cache clear image caches without scanning
--compliance string compliance report to generate (k8s-nsa,k8s-cis,k8s-pss-baseline,k8s-pss-restricted) --compliance string compliance report to generate (k8s-nsa,k8s-cis,k8s-pss-baseline,k8s-pss-restricted)
--components strings specify which components to scan (default [workload,infra]) --components strings specify which components to scan (workload,infra) (default [workload,infra])
--config-data strings specify paths from which data for the Rego policies will be recursively loaded --config-data strings specify paths from which data for the Rego policies will be recursively loaded
--config-policy strings specify the paths to the Rego policy files or to the directories containing them, applying config files --config-policy strings specify the paths to the Rego policy files or to the directories containing them, applying config files
--context string specify a context to scan --context string specify a context to scan
@@ -72,13 +72,13 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg:
--redis-tls enable redis TLS with public certificates, 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 --registry-token string registry token
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --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") --report string specify a report format for the output (all,summary) (default "all")
--reset remove all caches and database --reset remove all caches and database
--reset-policy-bundle remove policy bundle --reset-policy-bundle remove policy bundle
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor)
--scanners string comma-separated list of what security issues to detect (vuln,config,secret,license) (default "vuln,config,secret,rbac") --scanners string comma-separated list of what security issues to detect (vuln,config,secret,license) (default "vuln,config,secret,rbac")
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-db-update skip updating vulnerability database --skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal

View File

@@ -64,7 +64,7 @@ trivy repository [flags] REPO_URL
--scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret]) --scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret])
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode --server string server address in client mode
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-db-update skip updating vulnerability database --skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal

View File

@@ -69,7 +69,7 @@ trivy rootfs [flags] ROOTDIR
--scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret]) --scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret])
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode --server string server address in client mode
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-db-update skip updating vulnerability database --skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal

View File

@@ -49,7 +49,7 @@ trivy sbom [flags] SBOM_PATH
--reset remove all caches and database --reset remove all caches and database
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor)
--server string server address in client mode --server string server address in client mode
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-db-update skip updating vulnerability database --skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal

View File

@@ -61,7 +61,7 @@ trivy vm [flags] VM_IMAGE
--scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret]) --scanners strings comma-separated list of what security issues to detect (vuln,config,secret,license) (default [vuln,secret])
--secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml") --secret-config string specify a path to config file for secret scanning (default "trivy-secret.yaml")
--server string server address in client mode --server string server address in client mode
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL") -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--skip-db-update skip updating vulnerability database --skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories where the traversal is skipped --skip-dirs strings specify the directories where the traversal is skipped
--skip-files strings specify the file paths to skip traversal --skip-files strings specify the file paths to skip traversal

6
go.mod
View File

@@ -94,7 +94,7 @@ require (
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
go.uber.org/zap v1.24.0 go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/mod v0.11.0 golang.org/x/mod v0.12.0
golang.org/x/sync v0.3.0 golang.org/x/sync v0.3.0
golang.org/x/term v0.9.0 golang.org/x/term v0.9.0
golang.org/x/text v0.10.0 golang.org/x/text v0.10.0
@@ -359,9 +359,9 @@ require (
golang.org/x/crypto v0.10.0 // indirect golang.org/x/crypto v0.10.0 // indirect
golang.org/x/net v0.11.0 // indirect golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sys v0.9.0 // indirect golang.org/x/sys v0.10.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.8.0 // indirect golang.org/x/tools v0.10.0 // indirect
google.golang.org/api v0.121.0 // indirect google.golang.org/api v0.121.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect

12
go.sum
View File

@@ -1840,8 +1840,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2093,8 +2093,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -2197,8 +2197,8 @@ golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyj
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -17,8 +17,8 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
// Set a dummy path for the documents // Set a dummy path for the documents
flag.CacheDirFlag.Value = "/path/to/cache" flag.CacheDirFlag.Default = "/path/to/cache"
flag.ModuleDirFlag.Value = "$HOME/.trivy/modules" flag.ModuleDirFlag.Default = "$HOME/.trivy/modules"
cmd := commands.NewApp(ver) cmd := commands.NewApp(ver)
cmd.DisableAutoGenTag = true cmd.DisableAutoGenTag = true

View File

@@ -246,12 +246,12 @@ func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup := flag.NewReportFlagGroup() reportFlagGroup := flag.NewReportFlagGroup()
report := flag.ReportFormatFlag report := flag.ReportFormatFlag
report.Value = "summary" // override the default value as the summary is preferred for the compliance report report.Default = "summary" // override the default value as the summary is preferred for the compliance report
report.Usage = "specify a format for the compliance report." // "--report" works only with "--compliance" report.Usage = "specify a format for the compliance report." // "--report" works only with "--compliance"
reportFlagGroup.ReportFormat = &report reportFlagGroup.ReportFormat = &report
compliance := flag.ComplianceFlag compliance := flag.ComplianceFlag
compliance.Usage += fmt.Sprintf(" (%s)", types.ComplianceDockerCIS) compliance.Values = []string{types.ComplianceDockerCIS}
reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand. reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
imageFlags := &flag.Flags{ imageFlags := &flag.Flags{
@@ -330,7 +330,7 @@ func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
func NewFilesystemCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { func NewFilesystemCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup := flag.NewReportFlagGroup() reportFlagGroup := flag.NewReportFlagGroup()
reportFormat := flag.ReportFormatFlag reportFormat := flag.ReportFormatFlag
reportFormat.Usage = "specify a compliance report format for the output. (all,summary)" //@TODO: support --report summary for non compliance reports reportFormat.Usage = "specify a compliance report format for the output" //@TODO: support --report summary for non compliance reports
reportFlagGroup.ReportFormat = &reportFormat reportFlagGroup.ReportFormat = &reportFormat
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol' reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
@@ -546,7 +546,7 @@ func NewClientCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
Name: "remote", Name: "remote",
ConfigName: "server.addr", ConfigName: "server.addr",
Shorthand: "", Shorthand: "",
Value: "http://localhost:4954", Default: "http://localhost:4954",
Usage: "server address", Usage: "server address",
} }
remoteFlags.ServerAddr = &remoteAddr // disable '--server' and enable '--remote' instead. remoteFlags.ServerAddr = &remoteAddr // disable '--server' and enable '--remote' instead.
@@ -643,7 +643,7 @@ func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs' reportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs'
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol' reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
reportFormat := flag.ReportFormatFlag reportFormat := flag.ReportFormatFlag
reportFormat.Usage = "specify a compliance report format for the output. (all,summary)" //@TODO: support --report summary for non compliance reports reportFormat.Usage = "specify a compliance report format for the output" //@TODO: support --report summary for non compliance reports
reportFlagGroup.ReportFormat = &reportFormat reportFlagGroup.ReportFormat = &reportFormat
scanFlags := &flag.ScanFlagGroup{ scanFlags := &flag.ScanFlagGroup{
@@ -880,7 +880,7 @@ func NewModuleCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
scanFlags := flag.NewScanFlagGroup() scanFlags := flag.NewScanFlagGroup()
scanners := flag.ScannersFlag scanners := flag.ScannersFlag
scanners.Value = fmt.Sprintf( // overwrite the default value scanners.Default = fmt.Sprintf( // overwrite the default value
"%s,%s,%s,%s", "%s,%s,%s,%s",
types.VulnerabilityScanner, types.VulnerabilityScanner,
types.MisconfigScanner, types.MisconfigScanner,
@@ -895,16 +895,21 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup := flag.NewReportFlagGroup() reportFlagGroup := flag.NewReportFlagGroup()
compliance := flag.ComplianceFlag compliance := flag.ComplianceFlag
compliance.Usage += fmt.Sprintf(" (%s,%s, %s, %s)", types.ComplianceK8sNsa, types.ComplianceK8sCIS, types.ComplianceK8sPSSBaseline, types.ComplianceK8sPSSRestricted) compliance.Values = []string{
types.ComplianceK8sNsa,
types.ComplianceK8sCIS,
types.ComplianceK8sPSSBaseline,
types.ComplianceK8sPSSRestricted,
}
reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand. reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol' reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
formatFlag := flag.FormatFlag formatFlag := flag.FormatFlag
formatFlag.Usage = "format (" + strings.Join([]string{ formatFlag.Values = []string{
r.FormatTable, r.FormatTable,
r.FormatJSON, r.FormatJSON,
r.FormatCycloneDX, r.FormatCycloneDX,
}, ", ") + ")" }
reportFlagGroup.Format = &formatFlag reportFlagGroup.Format = &formatFlag
k8sFlags := &flag.Flags{ k8sFlags := &flag.Flags{
@@ -968,7 +973,7 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
func NewAWSCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { func NewAWSCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
reportFlagGroup := flag.NewReportFlagGroup() reportFlagGroup := flag.NewReportFlagGroup()
compliance := flag.ComplianceFlag compliance := flag.ComplianceFlag
compliance.Usage += fmt.Sprintf(" (%s, %s)", types.ComplianceAWSCIS12, types.ComplianceAWSCIS14) compliance.Values = []string{types.ComplianceAWSCIS12, types.ComplianceAWSCIS14}
reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand. reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol' reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
@@ -1047,7 +1052,7 @@ func NewVMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
Region: &flag.Flag{ Region: &flag.Flag{
Name: "aws-region", Name: "aws-region",
ConfigName: "aws.region", ConfigName: "aws.region",
Value: "", Default: "",
Usage: "AWS region to scan", Usage: "AWS region to scan",
}, },
}, },

View File

@@ -2,10 +2,16 @@ package commands
import ( import (
"bytes" "bytes"
"io"
"testing" "testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/report"
) )
func Test_showVersion(t *testing.T) { func Test_showVersion(t *testing.T) {
@@ -161,3 +167,128 @@ Policy Bundle:
}) })
} }
} }
func TestFlags(t *testing.T) {
type want struct {
format string
severities []dbTypes.Severity
}
tests := []struct {
name string
arguments []string // 1st argument is path to trivy binaries
want want
wantErr string
}{
{
name: "happy path",
arguments: []string{
"test",
},
want: want{
format: report.FormatTable,
severities: []dbTypes.Severity{
dbTypes.SeverityUnknown,
dbTypes.SeverityLow,
dbTypes.SeverityMedium,
dbTypes.SeverityHigh,
dbTypes.SeverityCritical,
},
},
},
{
name: "happy path with comma-separated severities",
arguments: []string{
"test",
"--severity",
"LOW,MEDIUM",
},
want: want{
format: report.FormatTable,
severities: []dbTypes.Severity{
dbTypes.SeverityLow,
dbTypes.SeverityMedium,
},
},
},
{
name: "happy path with repeated severities",
arguments: []string{
"test",
"--severity",
"LOW",
"--severity",
"HIGH",
},
want: want{
format: report.FormatTable,
severities: []dbTypes.Severity{
dbTypes.SeverityLow,
dbTypes.SeverityHigh,
},
},
},
{
name: "happy path with json",
arguments: []string{
"test",
"--format",
"json",
"--severity",
"CRITICAL",
},
want: want{
format: report.FormatJSON,
severities: []dbTypes.Severity{
dbTypes.SeverityCritical,
},
},
},
{
name: "invalid format",
arguments: []string{
"test",
"--format",
"foo",
},
wantErr: `invalid argument "foo" for "-f, --format" flag`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
globalFlags := flag.NewGlobalFlagGroup()
rootCmd := NewRootCommand("dev", globalFlags)
rootCmd.SetErr(io.Discard)
SetOut(io.Discard)
flags := &flag.Flags{
ReportFlagGroup: flag.NewReportFlagGroup(),
}
cmd := &cobra.Command{
Use: "test",
RunE: func(cmd *cobra.Command, args []string) error {
// Bind
require.NoError(t, flags.Bind(cmd))
options, err := flags.ToOptions("dev", args, globalFlags, nil)
require.NoError(t, err)
assert.Equal(t, tt.want.format, options.Format)
assert.Equal(t, tt.want.severities, options.Severities)
return nil
},
}
flags.AddFlags(cmd)
rootCmd.AddCommand(cmd)
rootCmd.SetArgs(tt.arguments)
err := rootCmd.Execute()
if tt.wantErr != "" {
assert.ErrorContains(t, err, tt.wantErr)
return
}
require.NoError(t, err)
})
}
}

View File

@@ -4,37 +4,37 @@ var (
awsRegionFlag = Flag{ awsRegionFlag = Flag{
Name: "region", Name: "region",
ConfigName: "cloud.aws.region", ConfigName: "cloud.aws.region",
Value: "", Default: "",
Usage: "AWS Region to scan", Usage: "AWS Region to scan",
} }
awsEndpointFlag = Flag{ awsEndpointFlag = Flag{
Name: "endpoint", Name: "endpoint",
ConfigName: "cloud.aws.endpoint", ConfigName: "cloud.aws.endpoint",
Value: "", Default: "",
Usage: "AWS Endpoint override", Usage: "AWS Endpoint override",
} }
awsServiceFlag = Flag{ awsServiceFlag = Flag{
Name: "service", Name: "service",
ConfigName: "cloud.aws.service", ConfigName: "cloud.aws.service",
Value: []string{}, Default: []string{},
Usage: "Only scan AWS Service(s) specified with this flag. Can specify multiple services using --service A --service B etc.", Usage: "Only scan AWS Service(s) specified with this flag. Can specify multiple services using --service A --service B etc.",
} }
awsSkipServicesFlag = Flag{ awsSkipServicesFlag = Flag{
Name: "skip-service", Name: "skip-service",
ConfigName: "cloud.aws.skip-service", ConfigName: "cloud.aws.skip-service",
Value: []string{}, Default: []string{},
Usage: "Skip selected AWS Service(s) specified with this flag. Can specify multiple services using --skip-service A --skip-service B etc.", Usage: "Skip selected AWS Service(s) specified with this flag. Can specify multiple services using --skip-service A --skip-service B etc.",
} }
awsAccountFlag = Flag{ awsAccountFlag = Flag{
Name: "account", Name: "account",
ConfigName: "cloud.aws.account", ConfigName: "cloud.aws.account",
Value: "", Default: "",
Usage: "The AWS account to scan. It's useful to specify this when reviewing cached results for multiple accounts.", Usage: "The AWS account to scan. It's useful to specify this when reviewing cached results for multiple accounts.",
} }
awsARNFlag = Flag{ awsARNFlag = Flag{
Name: "arn", Name: "arn",
ConfigName: "cloud.aws.arn", ConfigName: "cloud.aws.arn",
Value: "", Default: "",
Usage: "The AWS ARN to show results for. Useful to filter results once a scan is cached.", Usage: "The AWS ARN to show results for. Useful to filter results once a scan is cached.",
} }
) )

View File

@@ -22,43 +22,43 @@ var (
ClearCacheFlag = Flag{ ClearCacheFlag = Flag{
Name: "clear-cache", Name: "clear-cache",
ConfigName: "cache.clear", ConfigName: "cache.clear",
Value: false, Default: false,
Usage: "clear image caches without scanning", Usage: "clear image caches without scanning",
} }
CacheBackendFlag = Flag{ CacheBackendFlag = Flag{
Name: "cache-backend", Name: "cache-backend",
ConfigName: "cache.backend", ConfigName: "cache.backend",
Value: "fs", Default: "fs",
Usage: "cache backend (e.g. redis://localhost:6379)", Usage: "cache backend (e.g. redis://localhost:6379)",
} }
CacheTTLFlag = Flag{ CacheTTLFlag = Flag{
Name: "cache-ttl", Name: "cache-ttl",
ConfigName: "cache.ttl", ConfigName: "cache.ttl",
Value: time.Duration(0), Default: time.Duration(0),
Usage: "cache TTL when using redis as cache backend", Usage: "cache TTL when using redis as cache backend",
} }
RedisTLSFlag = Flag{ RedisTLSFlag = Flag{
Name: "redis-tls", Name: "redis-tls",
ConfigName: "cache.redis.tls", ConfigName: "cache.redis.tls",
Value: false, Default: false,
Usage: "enable redis TLS with public certificates, if using redis as cache backend", Usage: "enable redis TLS with public certificates, if using redis as cache backend",
} }
RedisCACertFlag = Flag{ RedisCACertFlag = Flag{
Name: "redis-ca", Name: "redis-ca",
ConfigName: "cache.redis.ca", ConfigName: "cache.redis.ca",
Value: "", Default: "",
Usage: "redis ca file location, if using redis as cache backend", Usage: "redis ca file location, if using redis as cache backend",
} }
RedisCertFlag = Flag{ RedisCertFlag = Flag{
Name: "redis-cert", Name: "redis-cert",
ConfigName: "cache.redis.cert", ConfigName: "cache.redis.cert",
Value: "", Default: "",
Usage: "redis certificate file location, if using redis as cache backend", Usage: "redis certificate file location, if using redis as cache backend",
} }
RedisKeyFlag = Flag{ RedisKeyFlag = Flag{
Name: "redis-key", Name: "redis-key",
ConfigName: "cache.redis.key", ConfigName: "cache.redis.key",
Value: "", Default: "",
Usage: "redis key file location, if using redis as cache backend", Usage: "redis key file location, if using redis as cache backend",
} }
) )

View File

@@ -6,13 +6,13 @@ var (
cloudUpdateCacheFlag = Flag{ cloudUpdateCacheFlag = Flag{
Name: "update-cache", Name: "update-cache",
ConfigName: "cloud.update-cache", ConfigName: "cloud.update-cache",
Value: false, Default: false,
Usage: "Update the cache for the applicable cloud provider instead of using cached results.", Usage: "Update the cache for the applicable cloud provider instead of using cached results.",
} }
cloudMaxCacheAgeFlag = Flag{ cloudMaxCacheAgeFlag = Flag{
Name: "max-cache-age", Name: "max-cache-age",
ConfigName: "cloud.max-cache-age", ConfigName: "cloud.max-cache-age",
Value: time.Hour * 24, Default: time.Hour * 24,
Usage: "The maximum age of the cloud cache. Cached data will be requeried from the cloud provider if it is older than this.", Usage: "The maximum age of the cloud cache. Cached data will be requeried from the cloud provider if it is older than this.",
} }
) )

View File

@@ -13,19 +13,19 @@ var (
ResetFlag = Flag{ ResetFlag = Flag{
Name: "reset", Name: "reset",
ConfigName: "reset", ConfigName: "reset",
Value: false, Default: false,
Usage: "remove all caches and database", Usage: "remove all caches and database",
} }
DownloadDBOnlyFlag = Flag{ DownloadDBOnlyFlag = Flag{
Name: "download-db-only", Name: "download-db-only",
ConfigName: "db.download-only", ConfigName: "db.download-only",
Value: false, Default: false,
Usage: "download/update vulnerability database but don't run a scan", Usage: "download/update vulnerability database but don't run a scan",
} }
SkipDBUpdateFlag = Flag{ SkipDBUpdateFlag = Flag{
Name: "skip-db-update", Name: "skip-db-update",
ConfigName: "db.skip-update", ConfigName: "db.skip-update",
Value: false, Default: false,
Usage: "skip updating vulnerability database", Usage: "skip updating vulnerability database",
Aliases: []Alias{ Aliases: []Alias{
{ {
@@ -37,37 +37,37 @@ var (
DownloadJavaDBOnlyFlag = Flag{ DownloadJavaDBOnlyFlag = Flag{
Name: "download-java-db-only", Name: "download-java-db-only",
ConfigName: "db.download-java-only", ConfigName: "db.download-java-only",
Value: false, Default: false,
Usage: "download/update Java index database but don't run a scan", Usage: "download/update Java index database but don't run a scan",
} }
SkipJavaDBUpdateFlag = Flag{ SkipJavaDBUpdateFlag = Flag{
Name: "skip-java-db-update", Name: "skip-java-db-update",
ConfigName: "db.java-skip-update", ConfigName: "db.java-skip-update",
Value: false, Default: false,
Usage: "skip updating Java index database", Usage: "skip updating Java index database",
} }
NoProgressFlag = Flag{ NoProgressFlag = Flag{
Name: "no-progress", Name: "no-progress",
ConfigName: "db.no-progress", ConfigName: "db.no-progress",
Value: false, Default: false,
Usage: "suppress progress bar", Usage: "suppress progress bar",
} }
DBRepositoryFlag = Flag{ DBRepositoryFlag = Flag{
Name: "db-repository", Name: "db-repository",
ConfigName: "db.repository", ConfigName: "db.repository",
Value: defaultDBRepository, Default: defaultDBRepository,
Usage: "OCI repository to retrieve trivy-db from", Usage: "OCI repository to retrieve trivy-db from",
} }
JavaDBRepositoryFlag = Flag{ JavaDBRepositoryFlag = Flag{
Name: "java-db-repository", Name: "java-db-repository",
ConfigName: "db.java-repository", ConfigName: "db.java-repository",
Value: defaultJavaDBRepository, Default: defaultJavaDBRepository,
Usage: "OCI repository to retrieve trivy-java-db from", Usage: "OCI repository to retrieve trivy-java-db from",
} }
LightFlag = Flag{ LightFlag = Flag{
Name: "light", Name: "light",
ConfigName: "db.light", ConfigName: "db.light",
Value: false, Default: false,
Usage: "deprecated", Usage: "deprecated",
Deprecated: true, Deprecated: true,
} }

View File

@@ -14,7 +14,7 @@ var (
Name: "config", Name: "config",
ConfigName: "config", ConfigName: "config",
Shorthand: "c", Shorthand: "c",
Value: "trivy.yaml", Default: "trivy.yaml",
Usage: "config path", Usage: "config path",
Persistent: true, Persistent: true,
} }
@@ -22,7 +22,7 @@ var (
Name: "version", Name: "version",
ConfigName: "version", ConfigName: "version",
Shorthand: "v", Shorthand: "v",
Value: false, Default: false,
Usage: "show version", Usage: "show version",
Persistent: true, Persistent: true,
} }
@@ -30,7 +30,7 @@ var (
Name: "quiet", Name: "quiet",
ConfigName: "quiet", ConfigName: "quiet",
Shorthand: "q", Shorthand: "q",
Value: false, Default: false,
Usage: "suppress progress bar and log output", Usage: "suppress progress bar and log output",
Persistent: true, Persistent: true,
} }
@@ -38,35 +38,35 @@ var (
Name: "debug", Name: "debug",
ConfigName: "debug", ConfigName: "debug",
Shorthand: "d", Shorthand: "d",
Value: false, Default: false,
Usage: "debug mode", Usage: "debug mode",
Persistent: true, Persistent: true,
} }
InsecureFlag = Flag{ InsecureFlag = Flag{
Name: "insecure", Name: "insecure",
ConfigName: "insecure", ConfigName: "insecure",
Value: false, Default: false,
Usage: "allow insecure server connections", Usage: "allow insecure server connections",
Persistent: true, Persistent: true,
} }
TimeoutFlag = Flag{ TimeoutFlag = Flag{
Name: "timeout", Name: "timeout",
ConfigName: "timeout", ConfigName: "timeout",
Value: time.Second * 300, // 5 mins Default: time.Second * 300, // 5 mins
Usage: "timeout", Usage: "timeout",
Persistent: true, Persistent: true,
} }
CacheDirFlag = Flag{ CacheDirFlag = Flag{
Name: "cache-dir", Name: "cache-dir",
ConfigName: "cache.dir", ConfigName: "cache.dir",
Value: fsutils.CacheDir(), Default: fsutils.CacheDir(),
Usage: "cache directory", Usage: "cache directory",
Persistent: true, Persistent: true,
} }
GenerateDefaultConfigFlag = Flag{ GenerateDefaultConfigFlag = Flag{
Name: "generate-default-config", Name: "generate-default-config",
ConfigName: "generate-default-config", ConfigName: "generate-default-config",
Value: false, Default: false,
Usage: "write the default config to trivy-default.yaml", Usage: "write the default config to trivy-default.yaml",
Persistent: true, Persistent: true,
} }

View File

@@ -2,7 +2,6 @@ package flag
import ( import (
v1 "github.com/google/go-containerregistry/pkg/v1" v1 "github.com/google/go-containerregistry/pkg/v1"
"golang.org/x/exp/slices"
"golang.org/x/xerrors" "golang.org/x/xerrors"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
@@ -18,38 +17,43 @@ var (
ImageConfigScannersFlag = Flag{ ImageConfigScannersFlag = Flag{
Name: "image-config-scanners", Name: "image-config-scanners",
ConfigName: "image.image-config-scanners", ConfigName: "image.image-config-scanners",
Value: "", Default: []string{},
Usage: "comma-separated list of what security issues to detect on container image configurations (config,secret)", Values: types.Scanners{
types.MisconfigScanner,
types.SecretScanner,
}.StringSlice(),
Usage: "comma-separated list of what security issues to detect on container image configurations",
} }
ScanRemovedPkgsFlag = Flag{ ScanRemovedPkgsFlag = Flag{
Name: "removed-pkgs", Name: "removed-pkgs",
ConfigName: "image.removed-pkgs", ConfigName: "image.removed-pkgs",
Value: false, Default: false,
Usage: "detect vulnerabilities of removed packages (only for Alpine)", Usage: "detect vulnerabilities of removed packages (only for Alpine)",
} }
InputFlag = Flag{ InputFlag = Flag{
Name: "input", Name: "input",
ConfigName: "image.input", ConfigName: "image.input",
Value: "", Default: "",
Usage: "input file path instead of image name", Usage: "input file path instead of image name",
} }
PlatformFlag = Flag{ PlatformFlag = Flag{
Name: "platform", Name: "platform",
ConfigName: "image.platform", ConfigName: "image.platform",
Value: "", Default: "",
Usage: "set platform in the form os/arch if image is multi-platform capable", Usage: "set platform in the form os/arch if image is multi-platform capable",
} }
DockerHostFlag = Flag{ DockerHostFlag = Flag{
Name: "docker-host", Name: "docker-host",
ConfigName: "image.docker.host", ConfigName: "image.docker.host",
Value: "", Default: "",
Usage: "unix domain socket path to use for docker scanning", Usage: "unix domain socket path to use for docker scanning",
} }
SourceFlag = Flag{ SourceFlag = Flag{
Name: "image-src", Name: "image-src",
ConfigName: "image.source", ConfigName: "image.source",
Value: ftypes.AllImageSources.StringSlice(), Default: ftypes.AllImageSources.StringSlice(),
Usage: "image source(s) to use, in priority order (docker,containerd,podman,remote)", Values: ftypes.AllImageSources.StringSlice(),
Usage: "image source(s) to use, in priority order",
} }
) )
@@ -98,16 +102,6 @@ func (f *ImageFlagGroup) Flags() []*Flag {
} }
func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) { func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
scanners, err := parseScanners(getStringSlice(f.ImageConfigScanners), types.AllImageConfigScanners)
if err != nil {
return ImageOptions{}, xerrors.Errorf("unable to parse image config scanners: %w", err)
}
imageSources, err := parseImageSources(getStringSlice(f.ImageSources))
if err != nil {
return ImageOptions{}, xerrors.Errorf("unable to parse image sources: %w", err)
}
var platform ftypes.Platform var platform ftypes.Platform
if p := getString(f.Platform); p != "" { if p := getString(f.Platform); p != "" {
pl, err := v1.ParsePlatform(p) pl, err := v1.ParsePlatform(p)
@@ -122,22 +116,10 @@ func (f *ImageFlagGroup) ToOptions() (ImageOptions, error) {
return ImageOptions{ return ImageOptions{
Input: getString(f.Input), Input: getString(f.Input),
ImageConfigScanners: scanners, ImageConfigScanners: getUnderlyingStringSlice[types.Scanner](f.ImageConfigScanners),
ScanRemovedPkgs: getBool(f.ScanRemovedPkgs), ScanRemovedPkgs: getBool(f.ScanRemovedPkgs),
Platform: platform, Platform: platform,
DockerHost: getString(f.DockerHost), DockerHost: getString(f.DockerHost),
ImageSources: imageSources, ImageSources: getUnderlyingStringSlice[ftypes.ImageSource](f.ImageSources),
}, nil }, nil
} }
func parseImageSources(srcs []string) (ftypes.ImageSources, error) {
var imageSources ftypes.ImageSources
for _, s := range srcs {
src := ftypes.ImageSource(s)
if !slices.Contains(ftypes.AllImageSources, src) {
return nil, xerrors.Errorf("unknown image source: %s", s)
}
imageSources = append(imageSources, src)
}
return imageSources, nil
}

View File

@@ -16,7 +16,7 @@ var (
ClusterContextFlag = Flag{ ClusterContextFlag = Flag{
Name: "context", Name: "context",
ConfigName: "kubernetes.context", ConfigName: "kubernetes.context",
Value: "", Default: "",
Usage: "specify a context to scan", Usage: "specify a context to scan",
Aliases: []Alias{ Aliases: []Alias{
{Name: "ctx"}, {Name: "ctx"},
@@ -26,19 +26,23 @@ var (
Name: "namespace", Name: "namespace",
ConfigName: "kubernetes.namespace", ConfigName: "kubernetes.namespace",
Shorthand: "n", Shorthand: "n",
Value: "", Default: "",
Usage: "specify a namespace to scan", Usage: "specify a namespace to scan",
} }
KubeConfigFlag = Flag{ KubeConfigFlag = Flag{
Name: "kubeconfig", Name: "kubeconfig",
ConfigName: "kubernetes.kubeconfig", ConfigName: "kubernetes.kubeconfig",
Value: "", Default: "",
Usage: "specify the kubeconfig file path to use", Usage: "specify the kubeconfig file path to use",
} }
ComponentsFlag = Flag{ ComponentsFlag = Flag{
Name: "components", Name: "components",
ConfigName: "kubernetes.components", ConfigName: "kubernetes.components",
Value: []string{ Default: []string{
"workload",
"infra",
},
Values: []string{
"workload", "workload",
"infra", "infra",
}, },
@@ -47,38 +51,38 @@ var (
K8sVersionFlag = Flag{ K8sVersionFlag = Flag{
Name: "k8s-version", Name: "k8s-version",
ConfigName: "kubernetes.k8s.version", ConfigName: "kubernetes.k8s.version",
Value: "", Default: "",
Usage: "specify k8s version to validate outdated api by it (example: 1.21.0)", Usage: "specify k8s version to validate outdated api by it (example: 1.21.0)",
} }
ParallelFlag = Flag{ ParallelFlag = Flag{
Name: "parallel", Name: "parallel",
ConfigName: "kubernetes.parallel", ConfigName: "kubernetes.parallel",
Value: 5, Default: 5,
Usage: "number (between 1-20) of goroutines enabled for parallel scanning", Usage: "number (between 1-20) of goroutines enabled for parallel scanning",
} }
TolerationsFlag = Flag{ TolerationsFlag = Flag{
Name: "tolerations", Name: "tolerations",
ConfigName: "kubernetes.tolerations", ConfigName: "kubernetes.tolerations",
Value: []string{}, Default: []string{},
Usage: "specify node-collector job tolerations (example: key1=value1:NoExecute,key2=value2:NoSchedule)", Usage: "specify node-collector job tolerations (example: key1=value1:NoExecute,key2=value2:NoSchedule)",
} }
AllNamespaces = Flag{ AllNamespaces = Flag{
Name: "all-namespaces", Name: "all-namespaces",
ConfigName: "kubernetes.all.namespaces", ConfigName: "kubernetes.all.namespaces",
Shorthand: "A", Shorthand: "A",
Value: false, Default: false,
Usage: "fetch resources from all cluster namespaces", Usage: "fetch resources from all cluster namespaces",
} }
NodeCollectorNamespace = Flag{ NodeCollectorNamespace = Flag{
Name: "node-collector-namespace", Name: "node-collector-namespace",
ConfigName: "node.collector.namespace", ConfigName: "node.collector.namespace",
Value: "trivy-temp", Default: "trivy-temp",
Usage: "specify the namespace in which the node-collector job should be deployed", Usage: "specify the namespace in which the node-collector job should be deployed",
} }
ExcludeNodes = Flag{ ExcludeNodes = Flag{
Name: "exclude-nodes", Name: "exclude-nodes",
ConfigName: "exclude.nodes", ConfigName: "exclude.nodes",
Value: []string{}, Default: []string{},
Usage: "indicate the node labels that the node-collector job should exclude from scanning (example: kubernetes.io/arch:arm64,team:dev)", Usage: "indicate the node labels that the node-collector job should exclude from scanning (example: kubernetes.io/arch:arm64,team:dev)",
} }
) )

View File

@@ -9,56 +9,56 @@ var (
LicenseFull = Flag{ LicenseFull = Flag{
Name: "license-full", Name: "license-full",
ConfigName: "license.full", ConfigName: "license.full",
Value: false, Default: false,
Usage: "eagerly look for licenses in source code headers and license files", Usage: "eagerly look for licenses in source code headers and license files",
} }
IgnoredLicenses = Flag{ IgnoredLicenses = Flag{
Name: "ignored-licenses", Name: "ignored-licenses",
ConfigName: "license.ignored", ConfigName: "license.ignored",
Value: []string{}, Default: []string{},
Usage: "specify a list of license to ignore", Usage: "specify a list of license to ignore",
} }
LicenseConfidenceLevel = Flag{ LicenseConfidenceLevel = Flag{
Name: "license-confidence-level", Name: "license-confidence-level",
ConfigName: "license.confidenceLevel", ConfigName: "license.confidenceLevel",
Value: 0.9, Default: 0.9,
Usage: "specify license classifier's confidence level", Usage: "specify license classifier's confidence level",
} }
// LicenseForbidden is an option only in a config file // LicenseForbidden is an option only in a config file
LicenseForbidden = Flag{ LicenseForbidden = Flag{
ConfigName: "license.forbidden", ConfigName: "license.forbidden",
Value: licensing.ForbiddenLicenses, Default: licensing.ForbiddenLicenses,
Usage: "forbidden licenses", Usage: "forbidden licenses",
} }
// LicenseRestricted is an option only in a config file // LicenseRestricted is an option only in a config file
LicenseRestricted = Flag{ LicenseRestricted = Flag{
ConfigName: "license.restricted", ConfigName: "license.restricted",
Value: licensing.RestrictedLicenses, Default: licensing.RestrictedLicenses,
Usage: "restricted licenses", Usage: "restricted licenses",
} }
// LicenseReciprocal is an option only in a config file // LicenseReciprocal is an option only in a config file
LicenseReciprocal = Flag{ LicenseReciprocal = Flag{
ConfigName: "license.reciprocal", ConfigName: "license.reciprocal",
Value: licensing.ReciprocalLicenses, Default: licensing.ReciprocalLicenses,
Usage: "reciprocal licenses", Usage: "reciprocal licenses",
} }
// LicenseNotice is an option only in a config file // LicenseNotice is an option only in a config file
LicenseNotice = Flag{ LicenseNotice = Flag{
ConfigName: "license.notice", ConfigName: "license.notice",
Value: licensing.NoticeLicenses, Default: licensing.NoticeLicenses,
Usage: "notice licenses", Usage: "notice licenses",
} }
// LicensePermissive is an option only in a config file // LicensePermissive is an option only in a config file
LicensePermissive = Flag{ LicensePermissive = Flag{
ConfigName: "license.permissive", ConfigName: "license.permissive",
Value: licensing.PermissiveLicenses, Default: licensing.PermissiveLicenses,
Usage: "permissive licenses", Usage: "permissive licenses",
} }
// LicenseUnencumbered is an option only in a config file // LicenseUnencumbered is an option only in a config file
LicenseUnencumbered = Flag{ LicenseUnencumbered = Flag{
ConfigName: "license.unencumbered", ConfigName: "license.unencumbered",
Value: licensing.UnencumberedLicenses, Default: licensing.UnencumberedLicenses,
Usage: "unencumbered licenses", Usage: "unencumbered licenses",
} }
) )

View File

@@ -10,49 +10,49 @@ var (
ResetPolicyBundleFlag = Flag{ ResetPolicyBundleFlag = Flag{
Name: "reset-policy-bundle", Name: "reset-policy-bundle",
ConfigName: "misconfiguration.reset-policy-bundle", ConfigName: "misconfiguration.reset-policy-bundle",
Value: false, Default: false,
Usage: "remove policy bundle", Usage: "remove policy bundle",
} }
IncludeNonFailuresFlag = Flag{ IncludeNonFailuresFlag = Flag{
Name: "include-non-failures", Name: "include-non-failures",
ConfigName: "misconfiguration.include-non-failures", ConfigName: "misconfiguration.include-non-failures",
Value: false, Default: false,
Usage: "include successes and exceptions, available with '--scanners config'", Usage: "include successes and exceptions, available with '--scanners config'",
} }
HelmValuesFileFlag = Flag{ HelmValuesFileFlag = Flag{
Name: "helm-values", Name: "helm-values",
ConfigName: "misconfiguration.helm.values", ConfigName: "misconfiguration.helm.values",
Value: []string{}, Default: []string{},
Usage: "specify paths to override the Helm values.yaml files", Usage: "specify paths to override the Helm values.yaml files",
} }
HelmSetFlag = Flag{ HelmSetFlag = Flag{
Name: "helm-set", Name: "helm-set",
ConfigName: "misconfiguration.helm.set", ConfigName: "misconfiguration.helm.set",
Value: []string{}, Default: []string{},
Usage: "specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)", Usage: "specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)",
} }
HelmSetFileFlag = Flag{ HelmSetFileFlag = Flag{
Name: "helm-set-file", Name: "helm-set-file",
ConfigName: "misconfiguration.helm.set-file", ConfigName: "misconfiguration.helm.set-file",
Value: []string{}, Default: []string{},
Usage: "specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)", Usage: "specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)",
} }
HelmSetStringFlag = Flag{ HelmSetStringFlag = Flag{
Name: "helm-set-string", Name: "helm-set-string",
ConfigName: "misconfiguration.helm.set-string", ConfigName: "misconfiguration.helm.set-string",
Value: []string{}, Default: []string{},
Usage: "specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)", Usage: "specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)",
} }
TfVarsFlag = Flag{ TfVarsFlag = Flag{
Name: "tf-vars", Name: "tf-vars",
ConfigName: "misconfiguration.terraform.vars", ConfigName: "misconfiguration.terraform.vars",
Value: []string{}, Default: []string{},
Usage: "specify paths to override the Terraform tfvars files", Usage: "specify paths to override the Terraform tfvars files",
} }
TerraformExcludeDownloaded = Flag{ TerraformExcludeDownloaded = Flag{
Name: "tf-exclude-downloaded-modules", Name: "tf-exclude-downloaded-modules",
ConfigName: "misconfiguration.terraform.exclude-downloaded-modules", ConfigName: "misconfiguration.terraform.exclude-downloaded-modules",
Value: false, Default: false,
Usage: "remove results for downloaded modules in .terraform folder", Usage: "remove results for downloaded modules in .terraform folder",
} }
) )

View File

@@ -14,14 +14,14 @@ var (
ModuleDirFlag = Flag{ ModuleDirFlag = Flag{
Name: "module-dir", Name: "module-dir",
ConfigName: "module.dir", ConfigName: "module.dir",
Value: module.DefaultDir, Default: module.DefaultDir,
Usage: "specify directory to the wasm modules that will be loaded", Usage: "specify directory to the wasm modules that will be loaded",
Persistent: true, Persistent: true,
} }
EnableModulesFlag = Flag{ EnableModulesFlag = Flag{
Name: "enable-modules", Name: "enable-modules",
ConfigName: "module.enable-modules", ConfigName: "module.enable-modules",
Value: []string{}, Default: []string{},
Usage: "[EXPERIMENTAL] module names to enable", Usage: "[EXPERIMENTAL] module names to enable",
Persistent: true, Persistent: true,
} }

View File

@@ -8,6 +8,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/samber/lo"
"github.com/spf13/cast" "github.com/spf13/cast"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@@ -21,6 +22,10 @@ import (
"github.com/aquasecurity/trivy/pkg/result" "github.com/aquasecurity/trivy/pkg/result"
) )
type String interface {
~string
}
type Flag struct { type Flag struct {
// Name is for CLI flag and environment variable. // Name is for CLI flag and environment variable.
// If this field is empty, it will be available only in config file. // If this field is empty, it will be available only in config file.
@@ -32,8 +37,12 @@ type Flag struct {
// Shorthand is a shorthand letter. // Shorthand is a shorthand letter.
Shorthand string Shorthand string
// Value is the default value. It must be filled to determine the flag type. // Default is the default value. It must be filled to determine the flag type.
Value interface{} Default any
// Values is a list of allowed values.
// It currently supports string flags and string slice flags only.
Values []string
// Usage explains how to use the flag. // Usage explains how to use the flag.
Usage string Usage string
@@ -178,13 +187,21 @@ func addFlag(cmd *cobra.Command, flag *Flag) {
flags = cmd.Flags() flags = cmd.Flags()
} }
switch v := flag.Value.(type) { switch v := flag.Default.(type) {
case int: case int:
flags.IntP(flag.Name, flag.Shorthand, v, flag.Usage) flags.IntP(flag.Name, flag.Shorthand, v, flag.Usage)
case string: case string:
flags.StringP(flag.Name, flag.Shorthand, v, flag.Usage) usage := flag.Usage
if len(flag.Values) > 0 {
usage += fmt.Sprintf(" (%s)", strings.Join(flag.Values, ","))
}
flags.VarP(newCustomStringValue(v, flag.Values), flag.Name, flag.Shorthand, usage)
case []string: case []string:
flags.StringSliceP(flag.Name, flag.Shorthand, v, flag.Usage) usage := flag.Usage
if len(flag.Values) > 0 {
usage += fmt.Sprintf(" (%s)", strings.Join(flag.Values, ","))
}
flags.VarP(newCustomStringSliceValue(v, flag.Values), flag.Name, flag.Shorthand, usage)
case bool: case bool:
flags.BoolP(flag.Name, flag.Shorthand, v, flag.Usage) flags.BoolP(flag.Name, flag.Shorthand, v, flag.Usage)
case time.Duration: case time.Duration:
@@ -203,7 +220,7 @@ func bind(cmd *cobra.Command, flag *Flag) error {
return nil return nil
} else if flag.Name == "" { } else if flag.Name == "" {
// This flag is available only in trivy.yaml // This flag is available only in trivy.yaml
viper.SetDefault(flag.ConfigName, flag.Value) viper.SetDefault(flag.ConfigName, flag.Default)
return nil return nil
} }
@@ -266,6 +283,16 @@ func getStringSlice(flag *Flag) []string {
return v return v
} }
func getUnderlyingStringSlice[T String](flag *Flag) []T {
ss := getStringSlice(flag)
if len(ss) == 0 {
return nil
}
return lo.Map(ss, func(s string, _ int) T {
return T(s)
})
}
func getInt(flag *Flag) int { func getInt(flag *Flag) int {
return cast.ToInt(getValue(flag)) return cast.ToInt(getValue(flag))
} }

View File

@@ -12,19 +12,19 @@ var (
UsernameFlag = Flag{ UsernameFlag = Flag{
Name: "username", Name: "username",
ConfigName: "registry.username", ConfigName: "registry.username",
Value: []string{}, Default: []string{},
Usage: "username. Comma-separated usernames allowed.", Usage: "username. Comma-separated usernames allowed.",
} }
PasswordFlag = Flag{ PasswordFlag = Flag{
Name: "password", Name: "password",
ConfigName: "registry.password", ConfigName: "registry.password",
Value: []string{}, Default: []string{},
Usage: "password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.", Usage: "password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.",
} }
RegistryTokenFlag = Flag{ RegistryTokenFlag = Flag{
Name: "registry-token", Name: "registry-token",
ConfigName: "registry.token", ConfigName: "registry.token",
Value: "", Default: "",
Usage: "registry token", Usage: "registry token",
} }
) )

View File

@@ -10,19 +10,19 @@ var (
SkipPolicyUpdateFlag = Flag{ SkipPolicyUpdateFlag = Flag{
Name: "skip-policy-update", Name: "skip-policy-update",
ConfigName: "rego.skip-policy-update", ConfigName: "rego.skip-policy-update",
Value: false, Default: false,
Usage: "skip fetching rego policy updates", Usage: "skip fetching rego policy updates",
} }
TraceFlag = Flag{ TraceFlag = Flag{
Name: "trace", Name: "trace",
ConfigName: "rego.trace", ConfigName: "rego.trace",
Value: false, Default: false,
Usage: "enable more verbose trace output for custom queries", Usage: "enable more verbose trace output for custom queries",
} }
ConfigPolicyFlag = Flag{ ConfigPolicyFlag = Flag{
Name: "config-policy", Name: "config-policy",
ConfigName: "rego.policy", ConfigName: "rego.policy",
Value: []string{}, Default: []string{},
Usage: "specify the paths to the Rego policy files or to the directories containing them, applying config files", Usage: "specify the paths to the Rego policy files or to the directories containing them, applying config files",
Aliases: []Alias{ Aliases: []Alias{
{Name: "policy"}, {Name: "policy"},
@@ -31,7 +31,7 @@ var (
ConfigDataFlag = Flag{ ConfigDataFlag = Flag{
Name: "config-data", Name: "config-data",
ConfigName: "rego.data", ConfigName: "rego.data",
Value: []string{}, Default: []string{},
Usage: "specify paths from which data for the Rego policies will be recursively loaded", Usage: "specify paths from which data for the Rego policies will be recursively loaded",
Aliases: []Alias{ Aliases: []Alias{
{Name: "data"}, {Name: "data"},
@@ -40,7 +40,7 @@ var (
PolicyNamespaceFlag = Flag{ PolicyNamespaceFlag = Flag{
Name: "policy-namespaces", Name: "policy-namespaces",
ConfigName: "rego.namespaces", ConfigName: "rego.namespaces",
Value: []string{}, Default: []string{},
Usage: "Rego namespaces", Usage: "Rego namespaces",
Aliases: []Alias{ Aliases: []Alias{
{Name: "namespaces"}, {Name: "namespaces"},

View File

@@ -15,31 +15,31 @@ var (
ServerTokenFlag = Flag{ ServerTokenFlag = Flag{
Name: "token", Name: "token",
ConfigName: "server.token", ConfigName: "server.token",
Value: "", Default: "",
Usage: "for authentication in client/server mode", Usage: "for authentication in client/server mode",
} }
ServerTokenHeaderFlag = Flag{ ServerTokenHeaderFlag = Flag{
Name: "token-header", Name: "token-header",
ConfigName: "server.token-header", ConfigName: "server.token-header",
Value: DefaultTokenHeader, Default: DefaultTokenHeader,
Usage: "specify a header name for token in client/server mode", Usage: "specify a header name for token in client/server mode",
} }
ServerAddrFlag = Flag{ ServerAddrFlag = Flag{
Name: "server", Name: "server",
ConfigName: "server.addr", ConfigName: "server.addr",
Value: "", Default: "",
Usage: "server address in client mode", Usage: "server address in client mode",
} }
ServerCustomHeadersFlag = Flag{ ServerCustomHeadersFlag = Flag{
Name: "custom-headers", Name: "custom-headers",
ConfigName: "server.custom-headers", ConfigName: "server.custom-headers",
Value: []string{}, Default: []string{},
Usage: "custom headers in client mode", Usage: "custom headers in client mode",
} }
ServerListenFlag = Flag{ ServerListenFlag = Flag{
Name: "listen", Name: "listen",
ConfigName: "server.listen", ConfigName: "server.listen",
Value: "localhost:4954", Default: "localhost:4954",
Usage: "listen address in server mode", Usage: "listen address in server mode",
} }
) )

View File

@@ -4,19 +4,19 @@ var (
FetchBranchFlag = Flag{ FetchBranchFlag = Flag{
Name: "branch", Name: "branch",
ConfigName: "repository.branch", ConfigName: "repository.branch",
Value: "", Default: "",
Usage: "pass the branch name to be scanned", Usage: "pass the branch name to be scanned",
} }
FetchCommitFlag = Flag{ FetchCommitFlag = Flag{
Name: "commit", Name: "commit",
ConfigName: "repository.commit", ConfigName: "repository.commit",
Value: "", Default: "",
Usage: "pass the commit hash to be scanned", Usage: "pass the commit hash to be scanned",
} }
FetchTagFlag = Flag{ FetchTagFlag = Flag{
Name: "tag", Name: "tag",
ConfigName: "repository.tag", ConfigName: "repository.tag",
Value: "", Default: "",
Usage: "pass the tag name to be scanned", Usage: "pass the tag name to be scanned",
} }
) )

View File

@@ -5,6 +5,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/samber/lo"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"golang.org/x/xerrors" "golang.org/x/xerrors"
@@ -26,76 +27,79 @@ var (
Name: "format", Name: "format",
ConfigName: "format", ConfigName: "format",
Shorthand: "f", Shorthand: "f",
Value: report.FormatTable, Default: report.FormatTable,
Usage: "format (" + strings.Join(report.SupportedFormats, ", ") + ")", Values: report.SupportedFormats,
Usage: "format",
} }
ReportFormatFlag = Flag{ ReportFormatFlag = Flag{
Name: "report", Name: "report",
ConfigName: "report", ConfigName: "report",
Value: "all", Default: "all",
Usage: "specify a report format for the output. (all,summary)", Values: []string{"all", "summary"},
Usage: "specify a report format for the output",
} }
TemplateFlag = Flag{ TemplateFlag = Flag{
Name: "template", Name: "template",
ConfigName: "template", ConfigName: "template",
Shorthand: "t", Shorthand: "t",
Value: "", Default: "",
Usage: "output template", Usage: "output template",
} }
DependencyTreeFlag = Flag{ DependencyTreeFlag = Flag{
Name: "dependency-tree", Name: "dependency-tree",
ConfigName: "dependency-tree", ConfigName: "dependency-tree",
Value: false, Default: false,
Usage: "[EXPERIMENTAL] show dependency origin tree of vulnerable packages", Usage: "[EXPERIMENTAL] show dependency origin tree of vulnerable packages",
} }
ListAllPkgsFlag = Flag{ ListAllPkgsFlag = Flag{
Name: "list-all-pkgs", Name: "list-all-pkgs",
ConfigName: "list-all-pkgs", ConfigName: "list-all-pkgs",
Value: false, Default: false,
Usage: "enabling the option will output all packages regardless of vulnerability", Usage: "enabling the option will output all packages regardless of vulnerability",
} }
IgnoreFileFlag = Flag{ IgnoreFileFlag = Flag{
Name: "ignorefile", Name: "ignorefile",
ConfigName: "ignorefile", ConfigName: "ignorefile",
Value: result.DefaultIgnoreFile, Default: result.DefaultIgnoreFile,
Usage: "specify .trivyignore file", Usage: "specify .trivyignore file",
} }
IgnorePolicyFlag = Flag{ IgnorePolicyFlag = Flag{
Name: "ignore-policy", Name: "ignore-policy",
ConfigName: "ignore-policy", ConfigName: "ignore-policy",
Value: "", Default: "",
Usage: "specify the Rego file path to evaluate each vulnerability", Usage: "specify the Rego file path to evaluate each vulnerability",
} }
ExitCodeFlag = Flag{ ExitCodeFlag = Flag{
Name: "exit-code", Name: "exit-code",
ConfigName: "exit-code", ConfigName: "exit-code",
Value: 0, Default: 0,
Usage: "specify exit code when any security issues are found", Usage: "specify exit code when any security issues are found",
} }
ExitOnEOLFlag = Flag{ ExitOnEOLFlag = Flag{
Name: "exit-on-eol", Name: "exit-on-eol",
ConfigName: "exit-on-eol", ConfigName: "exit-on-eol",
Value: 0, Default: 0,
Usage: "exit with the specified code when the OS reaches end of service/life", Usage: "exit with the specified code when the OS reaches end of service/life",
} }
OutputFlag = Flag{ OutputFlag = Flag{
Name: "output", Name: "output",
ConfigName: "output", ConfigName: "output",
Shorthand: "o", Shorthand: "o",
Value: "", Default: "",
Usage: "output file name", Usage: "output file name",
} }
SeverityFlag = Flag{ SeverityFlag = Flag{
Name: "severity", Name: "severity",
ConfigName: "severity", ConfigName: "severity",
Shorthand: "s", Shorthand: "s",
Value: strings.Join(dbTypes.SeverityNames, ","), Default: dbTypes.SeverityNames,
Usage: "severities of security issues to be displayed (comma separated)", Values: dbTypes.SeverityNames,
Usage: "severities of security issues to be displayed",
} }
ComplianceFlag = Flag{ ComplianceFlag = Flag{
Name: "compliance", Name: "compliance",
ConfigName: "scan.compliance", ConfigName: "scan.compliance",
Value: "", Default: "",
Usage: "compliance report to generate", Usage: "compliance report to generate",
} }
) )
@@ -177,10 +181,6 @@ func (f *ReportFlagGroup) ToOptions(out io.Writer) (ReportOptions, error) {
listAllPkgs := getBool(f.ListAllPkgs) listAllPkgs := getBool(f.ListAllPkgs)
output := getString(f.Output) output := getString(f.Output)
if format != "" && !slices.Contains(report.SupportedFormats, format) {
return ReportOptions{}, xerrors.Errorf("unknown format: %v", format)
}
if template != "" { if template != "" {
if format == "" { if format == "" {
log.Logger.Warn("'--template' is ignored because '--format template' is not specified. Use '--template' option with '--format template' option.") log.Logger.Warn("'--template' is ignored because '--format template' is not specified. Use '--template' option with '--format template' option.")
@@ -237,7 +237,7 @@ func (f *ReportFlagGroup) ToOptions(out io.Writer) (ReportOptions, error) {
ExitOnEOL: getInt(f.ExitOnEOL), ExitOnEOL: getInt(f.ExitOnEOL),
IgnorePolicy: getString(f.IgnorePolicy), IgnorePolicy: getString(f.IgnorePolicy),
Output: out, Output: out,
Severities: splitSeverity(getStringSlice(f.Severity)), Severities: toSeverity(getStringSlice(f.Severity)),
Compliance: cs, Compliance: cs,
}, nil }, nil
} }
@@ -272,23 +272,16 @@ func (f *ReportFlagGroup) forceListAllPkgs(format string, listAllPkgs, dependenc
return false return false
} }
func splitSeverity(severity []string) []dbTypes.Severity { func toSeverity(severity []string) []dbTypes.Severity {
switch { if len(severity) == 0 {
case len(severity) == 0:
return nil return nil
case len(severity) == 1 && strings.Contains(severity[0], ","): // get severities from flag
severity = strings.Split(severity[0], ",")
}
var severities []dbTypes.Severity
for _, s := range severity {
sev, err := dbTypes.NewSeverity(strings.ToUpper(s))
if err != nil {
log.Logger.Warnf("unknown severity option: %s", err)
continue
}
severities = append(severities, sev)
} }
severities := lo.Map(severity, func(s string, _ int) dbTypes.Severity {
// Note that there is no need to check the error here
// since the severity value is already validated in the flag parser.
sev, _ := dbTypes.NewSeverity(s)
return sev
})
log.Logger.Debugf("Severities: %q", severities) log.Logger.Debugf("Severities: %q", severities)
return severities return severities
} }

View File

@@ -48,33 +48,6 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
Output: os.Stdout, Output: os.Stdout,
}, },
}, },
{
name: "happy path with a low case severity",
fields: fields{
severities: "critical",
},
want: flag.ReportOptions{
Output: os.Stdout,
Severities: []dbTypes.Severity{
dbTypes.SeverityCritical,
},
},
},
{
name: "happy path with an unknown severity",
fields: fields{
severities: "CRITICAL,INVALID",
},
want: flag.ReportOptions{
Output: os.Stdout,
Severities: []dbTypes.Severity{
dbTypes.SeverityCritical,
},
},
wantLogs: []string{
"unknown severity option: unknown severity: INVALID",
},
},
{ {
name: "happy path with an cyclonedx", name: "happy path with an cyclonedx",
fields: fields{ fields: fields{
@@ -179,7 +152,7 @@ func TestReportFlagGroup_ToOptions(t *testing.T) {
name: "happy path with compliance", name: "happy path with compliance",
fields: fields{ fields: fields{
compliane: "@testdata/example-spec.yaml", compliane: "@testdata/example-spec.yaml",
severities: "low", severities: dbTypes.SeverityLow.String(),
}, },
want: flag.ReportOptions{ want: flag.ReportOptions{
Output: os.Stdout, Output: os.Stdout,

View File

@@ -10,21 +10,21 @@ var (
ArtifactTypeFlag = Flag{ ArtifactTypeFlag = Flag{
Name: "artifact-type", Name: "artifact-type",
ConfigName: "sbom.artifact-type", ConfigName: "sbom.artifact-type",
Value: "", Default: "",
Usage: "deprecated", Usage: "deprecated",
Deprecated: true, Deprecated: true,
} }
SBOMFormatFlag = Flag{ SBOMFormatFlag = Flag{
Name: "sbom-format", Name: "sbom-format",
ConfigName: "sbom.format", ConfigName: "sbom.format",
Value: "", Default: "",
Usage: "deprecated", Usage: "deprecated",
Deprecated: true, Deprecated: true,
} }
VEXFlag = Flag{ VEXFlag = Flag{
Name: "vex", Name: "vex",
ConfigName: "sbom.vex", ConfigName: "sbom.vex",
Value: "", Default: "",
Usage: "[EXPERIMENTAL] file path to VEX", Usage: "[EXPERIMENTAL] file path to VEX",
} }
) )

View File

@@ -1,9 +1,6 @@
package flag package flag
import ( import (
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/types"
) )
@@ -11,28 +8,34 @@ var (
SkipDirsFlag = Flag{ SkipDirsFlag = Flag{
Name: "skip-dirs", Name: "skip-dirs",
ConfigName: "scan.skip-dirs", ConfigName: "scan.skip-dirs",
Value: []string{}, Default: []string{},
Usage: "specify the directories where the traversal is skipped", Usage: "specify the directories where the traversal is skipped",
} }
SkipFilesFlag = Flag{ SkipFilesFlag = Flag{
Name: "skip-files", Name: "skip-files",
ConfigName: "scan.skip-files", ConfigName: "scan.skip-files",
Value: []string{}, Default: []string{},
Usage: "specify the file paths to skip traversal", Usage: "specify the file paths to skip traversal",
} }
OfflineScanFlag = Flag{ OfflineScanFlag = Flag{
Name: "offline-scan", Name: "offline-scan",
ConfigName: "scan.offline", ConfigName: "scan.offline",
Value: false, Default: false,
Usage: "do not issue API requests to identify dependencies", Usage: "do not issue API requests to identify dependencies",
} }
ScannersFlag = Flag{ ScannersFlag = Flag{
Name: "scanners", Name: "scanners",
ConfigName: "scan.scanners", ConfigName: "scan.scanners",
Value: types.Scanners{ Default: types.Scanners{
types.VulnerabilityScanner, types.VulnerabilityScanner,
types.SecretScanner, types.SecretScanner,
}.StringSlice(), }.StringSlice(),
Values: types.Scanners{
types.VulnerabilityScanner,
types.MisconfigScanner,
types.SecretScanner,
types.LicenseScanner,
}.StringSlice(),
Aliases: []Alias{ Aliases: []Alias{
{ {
Name: "security-checks", Name: "security-checks",
@@ -40,36 +43,37 @@ var (
Deprecated: true, // --security-checks was renamed to --scanners Deprecated: true, // --security-checks was renamed to --scanners
}, },
}, },
Usage: "comma-separated list of what security issues to detect (vuln,config,secret,license)", Usage: "comma-separated list of what security issues to detect",
} }
FilePatternsFlag = Flag{ FilePatternsFlag = Flag{
Name: "file-patterns", Name: "file-patterns",
ConfigName: "scan.file-patterns", ConfigName: "scan.file-patterns",
Value: []string{}, Default: []string{},
Usage: "specify config file patterns", Usage: "specify config file patterns",
} }
SlowFlag = Flag{ SlowFlag = Flag{
Name: "slow", Name: "slow",
ConfigName: "scan.slow", ConfigName: "scan.slow",
Value: false, Default: false,
Usage: "scan over time with lower CPU and memory utilization", Usage: "scan over time with lower CPU and memory utilization",
} }
SBOMSourcesFlag = Flag{ SBOMSourcesFlag = Flag{
Name: "sbom-sources", Name: "sbom-sources",
ConfigName: "scan.sbom-sources", ConfigName: "scan.sbom-sources",
Value: []string{}, Default: []string{},
Usage: "[EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor)", Values: []string{"oci", "rekor"},
Usage: "[EXPERIMENTAL] try to retrieve SBOM from the specified sources",
} }
RekorURLFlag = Flag{ RekorURLFlag = Flag{
Name: "rekor-url", Name: "rekor-url",
ConfigName: "scan.rekor-url", ConfigName: "scan.rekor-url",
Value: "https://rekor.sigstore.dev", Default: "https://rekor.sigstore.dev",
Usage: "[EXPERIMENTAL] address of rekor STL server", Usage: "[EXPERIMENTAL] address of rekor STL server",
} }
IncludeDevDepsFlag = Flag{ IncludeDevDepsFlag = Flag{
Name: "include-dev-deps", Name: "include-dev-deps",
ConfigName: "include-dev-deps", ConfigName: "include-dev-deps",
Value: false, Default: false,
Usage: "include development dependencies in the report (supported: npm)", Usage: "include development dependencies in the report (supported: npm)",
} }
) )
@@ -136,47 +140,17 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
if len(args) == 1 { if len(args) == 1 {
target = args[0] target = args[0]
} }
scanners, err := parseScanners(getStringSlice(f.Scanners), types.AllScanners)
if err != nil {
return ScanOptions{}, xerrors.Errorf("unable to parse scanners: %w", err)
}
sbomSources := getStringSlice(f.SBOMSources)
if err = validateSBOMSources(sbomSources); err != nil {
return ScanOptions{}, xerrors.Errorf("unable to parse SBOM sources: %w", err)
}
return ScanOptions{ return ScanOptions{
Target: target, Target: target,
SkipDirs: getStringSlice(f.SkipDirs), SkipDirs: getStringSlice(f.SkipDirs),
SkipFiles: getStringSlice(f.SkipFiles), SkipFiles: getStringSlice(f.SkipFiles),
OfflineScan: getBool(f.OfflineScan), OfflineScan: getBool(f.OfflineScan),
Scanners: scanners, Scanners: getUnderlyingStringSlice[types.Scanner](f.Scanners),
FilePatterns: getStringSlice(f.FilePatterns), FilePatterns: getStringSlice(f.FilePatterns),
Slow: getBool(f.Slow), Slow: getBool(f.Slow),
SBOMSources: sbomSources, SBOMSources: getStringSlice(f.SBOMSources),
RekorURL: getString(f.RekorURL), RekorURL: getString(f.RekorURL),
IncludeDevDeps: getBool(f.IncludeDevDeps), IncludeDevDeps: getBool(f.IncludeDevDeps),
}, nil }, nil
} }
func parseScanners(scanner []string, allowedScanners []types.Scanner) (types.Scanners, error) {
var scanners types.Scanners
for _, v := range scanner {
s := types.Scanner(v)
if !slices.Contains(allowedScanners, s) {
return nil, xerrors.Errorf("unknown scanner: %s", v)
}
scanners = append(scanners, s)
}
return scanners, nil
}
func validateSBOMSources(sbomSources []string) error {
for _, v := range sbomSources {
if !slices.Contains(types.SBOMSources, v) {
return xerrors.Errorf("unknown SBOM source: %s", v)
}
}
return nil
}

View File

@@ -46,16 +46,6 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
}, },
assertion: require.NoError, assertion: require.NoError,
}, },
{
name: "with wrong scanner",
fields: fields{
scanners: "vuln,WRONG-CHECK",
},
want: flag.ScanOptions{},
assertion: func(t require.TestingT, err error, msgs ...interface{}) {
require.ErrorContains(t, err, "unknown scanner: WRONG-CHECK")
},
},
{ {
name: "without target (args)", name: "without target (args)",
args: []string{}, args: []string{},

View File

@@ -4,7 +4,7 @@ var (
SecretConfigFlag = Flag{ SecretConfigFlag = Flag{
Name: "secret-config", Name: "secret-config",
ConfigName: "secret.config", ConfigName: "secret.config",
Value: "trivy-secret.yaml", Default: "trivy-secret.yaml",
Usage: "specify a path to config file for secret scanning", Usage: "specify a path to config file for secret scanning",
} }
) )

91
pkg/flag/value.go Normal file
View File

@@ -0,0 +1,91 @@
package flag
import (
"strings"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
)
// -- string Value
type customStringValue struct {
value *string
allowed []string
}
func newCustomStringValue(val string, allowed []string) *customStringValue {
return &customStringValue{
value: &val,
allowed: allowed,
}
}
func (s *customStringValue) Set(val string) error {
if len(s.allowed) > 0 && !slices.Contains(s.allowed, val) {
return xerrors.Errorf("must be one of %q", s.allowed)
}
s.value = &val
return nil
}
func (s *customStringValue) Type() string {
return "string"
}
func (s *customStringValue) String() string { return *s.value }
// -- stringSlice Value
type customStringSliceValue struct {
value *[]string
allowed []string
changed bool
}
func newCustomStringSliceValue(val []string, allowed []string) *customStringSliceValue {
return &customStringSliceValue{
value: &val,
allowed: allowed,
}
}
func (s *customStringSliceValue) Set(val string) error {
values := strings.Split(val, ",")
for _, v := range values {
if len(s.allowed) > 0 && !slices.Contains(s.allowed, v) {
return xerrors.Errorf("must be one of %q", s.allowed)
}
}
if !s.changed {
*s.value = values
} else {
*s.value = append(*s.value, values...)
}
s.changed = true
return nil
}
func (s *customStringSliceValue) Type() string {
return "stringSlice"
}
func (s *customStringSliceValue) String() string {
if len(*s.value) == 0 {
// "[]" is not recognized as a zero value
// cf. https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/flag.go#L553-L565
return ""
}
return "[" + strings.Join(*s.value, ",") + "]"
}
func (s *customStringSliceValue) Append(val string) error {
s.changed = true
return s.Set(val)
}
func (s *customStringSliceValue) Replace(val []string) error {
*s.value = val
return nil
}
func (s *customStringSliceValue) GetSlice() []string {
return *s.value
}

View File

@@ -11,16 +11,20 @@ var (
VulnTypeFlag = Flag{ VulnTypeFlag = Flag{
Name: "vuln-type", Name: "vuln-type",
ConfigName: "vulnerability.type", ConfigName: "vulnerability.type",
Value: []string{ Default: []string{
types.VulnTypeOS, types.VulnTypeOS,
types.VulnTypeLibrary, types.VulnTypeLibrary,
}, },
Usage: "comma-separated list of vulnerability types (os,library)", Values: []string{
types.VulnTypeOS,
types.VulnTypeLibrary,
},
Usage: "comma-separated list of vulnerability types",
} }
IgnoreUnfixedFlag = Flag{ IgnoreUnfixedFlag = Flag{
Name: "ignore-unfixed", Name: "ignore-unfixed",
ConfigName: "vulnerability.ignore-unfixed", ConfigName: "vulnerability.ignore-unfixed",
Value: false, Default: false,
Usage: "display only fixed vulnerabilities", Usage: "display only fixed vulnerabilities",
} }
) )