mirror of
https://github.com/aquasecurity/trivy.git
synced 2026-01-31 13:53:14 +08:00
feat(report): support OPA to filter vulnerabilities (#562)
* feat(cli): add --filter option * feat(opa): support OPA * test(opa): add a test case with OPA * test: update a mock * chore(mod): update dependencies * chore(filter): add example Rego files * chore(README): update * chore(rego): apply opa fmt * refactor: replace filter with policy * chore(policy): update rego files * fix(vulnerability): evaluate each vulnerability * chore(README): update * Update README.md Co-authored-by: Itay Shakury <itay@itaysk.com> * Update README.md Co-authored-by: Itay Shakury <itay@itaysk.com> * chore(README): update a TOC link * fix: replace allow with ignore * chore(README): update Co-authored-by: Itay Shakury <itay@itaysk.com>
This commit is contained in:
36
README.md
36
README.md
@@ -44,6 +44,7 @@ A Simple and Comprehensive Vulnerability Scanner for Containers and other Artifa
|
||||
+ [Save the results using a template](#save-the-results-using-a-template)
|
||||
+ [Filter the vulnerabilities by severities](#filter-the-vulnerabilities-by-severities)
|
||||
+ [Filter the vulnerabilities by type](#filter-the-vulnerabilities-by-type)
|
||||
+ [Filter the vulnerabilities by Open Policy Agent](#filter-the-vulnerabilities-by-open-policy-agent-policy)
|
||||
+ [Skip update of vulnerability DB](#skip-update-of-vulnerability-db)
|
||||
+ [Only download vulnerability database](#only-download-vulnerability-database)
|
||||
+ [Ignore unfixed vulnerabilities](#ignore-unfixed-vulnerabilities)
|
||||
@@ -1098,6 +1099,41 @@ Total: 4751 (UNKNOWN: 1, LOW: 150, MEDIUM: 3504, HIGH: 1013, CRITICAL: 83)
|
||||
|
||||
</details>
|
||||
|
||||
### Filter the vulnerabilities by Open Policy Agent policy
|
||||
[EXPERIMENTAL] This feature might change without preserving backwards compatibility.
|
||||
|
||||
Trivy supports Open Policy Agent (OPA) to filter vulnerabilities. You can specify a Rego file with `--ignore-policy` option.
|
||||
|
||||
The Rego package name must be `trivy` and it must include a rule called `ignore` which determines if each individual vulnerability should be excluded (ignore=true) or not (ignore=false). In the policy, each vulnerability will be available for inspection as the `input` variable. The structure of each vulnerability input is the same as for the Trivy JSON output.
|
||||
There is a built-in Rego library with helper functions that you can import into your policy using: `import data.lib.trivy`. For more info about the helper functions, look at the library [here](pkg/vulnerability/module.go)
|
||||
|
||||
To get started, see the [example policy](./contrib/example_policy).
|
||||
|
||||
```
|
||||
$ trivy image --policy contrib/example_filter/basic.rego centos:7
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
```
|
||||
centos:7 (centos 7.8.2003)
|
||||
==========================
|
||||
Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)
|
||||
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------+
|
||||
| glib2 | CVE-2016-3191 | HIGH | 2.56.1-5.el7 | | pcre: workspace overflow |
|
||||
| | | | | | for (*ACCEPT) with deeply |
|
||||
| | | | | | nested parentheses (8.39/13, |
|
||||
| | | | | | 10.22/12) |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------+
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Skip update of vulnerability DB
|
||||
|
||||
`Trivy` always updates its vulnerability database when it starts operating. This is usually fast, as it is a difference update. But if you want to skip even that, use the `--skip-update` option.
|
||||
|
||||
94
contrib/example_policy/advanced.rego
Normal file
94
contrib/example_policy/advanced.rego
Normal file
@@ -0,0 +1,94 @@
|
||||
package trivy
|
||||
|
||||
import data.lib.trivy
|
||||
|
||||
default ignore = false
|
||||
|
||||
nvd_v3_vector = v {
|
||||
v := input.CVSS.nvd.v3
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires high privilege
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.PrivilegesRequired == "High"
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires user interaction
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.UserInteraction == "Required"
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "openssl"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate Attack Vector
|
||||
ignore_attack_vectors := {"Physical", "Local"}
|
||||
cvss_vector.AttackVector == ignore_attack_vectors[_]
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "openssl"
|
||||
|
||||
# Evaluate severity
|
||||
input.Severity == {"LOW", "MEDIUM", "HIGH"}[_]
|
||||
|
||||
# Evaluate CWE-ID
|
||||
deny_cwe_ids := {
|
||||
"CWE-119", # Improper Restriction of Operations within the Bounds of a Memory Buffer
|
||||
"CWE-200", # Exposure of Sensitive Information to an Unauthorized Actor
|
||||
}
|
||||
|
||||
count({x | x := input.CweIDs[_]; x == deny_cwe_ids[_]}) == 0
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "bash"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate Attack Vector
|
||||
ignore_attack_vectors := {"Physical", "Local", "Adjacent"}
|
||||
cvss_vector.AttackVector == ignore_attack_vectors[_]
|
||||
|
||||
# Evaluate severity
|
||||
input.Severity == {"LOW", "MEDIUM", "HIGH"}[_]
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "django"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate Attack Vector
|
||||
ignore_attack_vectors := {"Physical", "Local"}
|
||||
cvss_vector.AttackVector == ignore_attack_vectors[_]
|
||||
|
||||
# Evaluate severity
|
||||
input.Severity == {"LOW", "MEDIUM"}[_]
|
||||
|
||||
# Evaluate CWE-ID
|
||||
deny_cwe_ids := {
|
||||
"CWE-89", # SQL Injection
|
||||
"CWE-78", # OS Command Injection
|
||||
}
|
||||
|
||||
count({x | x := input.CweIDs[_]; x == deny_cwe_ids[_]}) == 0
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == "jquery"
|
||||
|
||||
# Split CVSSv3 vector
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
|
||||
# Evaluate CWE-ID
|
||||
deny_cwe_ids := {"CWE-79"} # XSS
|
||||
count({x | x := input.CweIDs[_]; x == deny_cwe_ids[_]}) == 0
|
||||
}
|
||||
45
contrib/example_policy/basic.rego
Normal file
45
contrib/example_policy/basic.rego
Normal file
@@ -0,0 +1,45 @@
|
||||
package trivy
|
||||
|
||||
import data.lib.trivy
|
||||
|
||||
default ignore = false
|
||||
|
||||
ignore_pkgs := {"bash", "bind-license", "rpm", "vim", "vim-minimal"}
|
||||
|
||||
ignore_severities := {"LOW", "MEDIUM"}
|
||||
|
||||
nvd_v3_vector = v {
|
||||
v := input.CVSS.nvd.v3
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.PkgName == ignore_pkgs[_]
|
||||
}
|
||||
|
||||
ignore {
|
||||
input.Severity == ignore_severities[_]
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which is not remotely exploitable
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.AttackVector != "Network"
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires high privilege
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.PrivilegesRequired == "High"
|
||||
}
|
||||
|
||||
# Ignore a vulnerability which requires user interaction
|
||||
ignore {
|
||||
cvss_vector := trivy.parse_cvss_vector_v3(nvd_v3_vector)
|
||||
cvss_vector.UserInteraction == "Required"
|
||||
}
|
||||
|
||||
# Ignore CSRF
|
||||
ignore {
|
||||
# https://cwe.mitre.org/data/definitions/352.html
|
||||
input.CweIDs[_] == "CWE-352"
|
||||
}
|
||||
1
go.mod
1
go.mod
@@ -22,6 +22,7 @@ require (
|
||||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
|
||||
github.com/open-policy-agent/opa v0.21.1
|
||||
github.com/spf13/afero v1.2.2
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/testcontainers/testcontainers-go v0.3.1
|
||||
|
||||
24
go.sum
24
go.sum
@@ -36,6 +36,8 @@ github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tT
|
||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.7 h1:fzrmmkskv067ZQbd9wERNGuxckWw67dyzoMG62p7LMo=
|
||||
github.com/OneOfOne/xxhash v1.2.7/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
@@ -149,6 +151,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
@@ -183,10 +187,13 @@ github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -195,6 +202,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v0.0.0-20181025225059-d3de96c4c28e/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -229,6 +237,7 @@ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v0.0.0-20181024020800-521ea7b17d02/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
@@ -314,6 +323,7 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-jsonpointer v0.0.0-20180225143300-37667080efed/go.mod h1:SDJ4hurDYyQ9/7nc+eCYtXqdufgK4Cq9TJlwPklqEYA=
|
||||
github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k=
|
||||
@@ -350,6 +360,8 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/open-policy-agent/opa v0.21.1 h1:c4lUnB0mO2KssiUnyh6Y9IGhggvXI3EgObkmhVTvEqQ=
|
||||
github.com/open-policy-agent/opa v0.21.1/go.mod h1:cZaTfhxsj7QdIiUI0U9aBtOLLTqVNe+XE60+9kZKLHw=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
@@ -361,6 +373,8 @@ github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmU
|
||||
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -369,14 +383,18 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.0.0-20181025174421-f30f42803563/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
@@ -412,10 +430,12 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.0-20181021141114-fe5e611709b0/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v0.0.0-20181024212040-082b515c9490/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -450,6 +470,8 @@ github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b h1:vVRagRXf67ESqAb72hG2C/ZwI8NtJF2u2V76EsuOHGY=
|
||||
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
@@ -484,6 +506,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/lint v0.0.0-20181023182221-1baf3a9d7d67/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -603,6 +626,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
||||
@@ -176,6 +176,12 @@ var (
|
||||
EnvVars: []string{"TRIVY_TOKEN_HEADER"},
|
||||
}
|
||||
|
||||
ignorePolicy = cli.StringFlag{
|
||||
Name: "ignore-policy",
|
||||
Usage: "specify the Rego file to evaluate each vulnerability",
|
||||
EnvVars: []string{"TRIVY_IGNORE_POLICY"},
|
||||
}
|
||||
|
||||
globalFlags = []cli.Flag{
|
||||
&quietFlag,
|
||||
&debugFlag,
|
||||
@@ -200,6 +206,7 @@ var (
|
||||
&ignoreFileFlag,
|
||||
&timeoutFlag,
|
||||
&lightFlag,
|
||||
&ignorePolicy,
|
||||
}
|
||||
|
||||
// deprecated options
|
||||
@@ -352,6 +359,7 @@ func NewFilesystemCommand() *cli.Command {
|
||||
&cacheDirFlag,
|
||||
&timeoutFlag,
|
||||
&noProgressFlag,
|
||||
&ignorePolicy,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -380,6 +388,7 @@ func NewRepositoryCommand() *cli.Command {
|
||||
&cacheDirFlag,
|
||||
&timeoutFlag,
|
||||
&noProgressFlag,
|
||||
&ignorePolicy,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -407,6 +416,7 @@ func NewClientCommand() *cli.Command {
|
||||
&ignoreFileFlag,
|
||||
&cacheDirFlag,
|
||||
&timeoutFlag,
|
||||
&ignorePolicy,
|
||||
|
||||
// original flags
|
||||
&token,
|
||||
|
||||
@@ -86,8 +86,12 @@ func run(c config.Config, initializeScanner InitializeScanner) error {
|
||||
vulnClient := initializeVulnerabilityClient()
|
||||
for i := range results {
|
||||
vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
|
||||
results[i].Vulnerabilities = vulnClient.Filter(results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile)
|
||||
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile, c.IgnorePolicy)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to filter vulnerabilities: %w", err)
|
||||
}
|
||||
results[i].Vulnerabilities = vulns
|
||||
}
|
||||
|
||||
if err = report.WriteResults(c.Format, c.Output, results, c.Template, c.Light); err != nil {
|
||||
|
||||
@@ -79,8 +79,12 @@ func run(c config.Config) (err error) {
|
||||
|
||||
vulnClient := initializeVulnerabilityClient()
|
||||
for i := range results {
|
||||
results[i].Vulnerabilities = vulnClient.Filter(results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile)
|
||||
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
c.Severities, c.IgnoreUnfixed, c.IgnoreFile, c.IgnorePolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results[i].Vulnerabilities = vulns
|
||||
}
|
||||
|
||||
if err = report.WriteResults(c.Format, c.Output, results, c.Template, false); err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ type ReportConfig struct {
|
||||
IgnoreFile string
|
||||
IgnoreUnfixed bool
|
||||
ExitCode int
|
||||
IgnorePolicy string
|
||||
|
||||
// these variables are not exported
|
||||
vulnType string
|
||||
@@ -32,9 +33,10 @@ type ReportConfig struct {
|
||||
|
||||
func NewReportConfig(c *cli.Context) ReportConfig {
|
||||
return ReportConfig{
|
||||
output: c.String("output"),
|
||||
Format: c.String("format"),
|
||||
Template: c.String("template"),
|
||||
output: c.String("output"),
|
||||
Format: c.String("format"),
|
||||
Template: c.String("template"),
|
||||
IgnorePolicy: c.String("ignore-policy"),
|
||||
|
||||
vulnType: c.String("vuln-type"),
|
||||
severities: c.String("severity"),
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestServer_Detect(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
detectExpectation library.OperationDetectExpectation
|
||||
fillInfoExpectation vulnerability.FillInfoExpectation
|
||||
fillInfoExpectation vulnerability.OperationFillInfoExpectation
|
||||
wantRes *proto.DetectResponse
|
||||
wantErr string
|
||||
}{
|
||||
@@ -80,8 +80,8 @@ func TestServer_Detect(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
fillInfoExpectation: vulnerability.FillInfoExpectation{
|
||||
Args: vulnerability.FillInfoArgs{
|
||||
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
|
||||
Args: vulnerability.OperationFillInfoArgs{
|
||||
Vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestServer_Detect(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
detectExpectation ospkg.DetectExpectation
|
||||
fillInfoExpectation vulnerability.FillInfoExpectation
|
||||
fillInfoExpectation vulnerability.OperationFillInfoExpectation
|
||||
wantRes *proto.DetectResponse
|
||||
wantErr string
|
||||
}{
|
||||
@@ -73,8 +73,8 @@ func TestServer_Detect(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
fillInfoExpectation: vulnerability.FillInfoExpectation{
|
||||
Args: vulnerability.FillInfoArgs{
|
||||
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
|
||||
Args: vulnerability.OperationFillInfoArgs{
|
||||
Vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
scanExpectation scanner.DriverScanExpectation
|
||||
fillInfoExpectation vulnerability.FillInfoExpectation
|
||||
fillInfoExpectation vulnerability.OperationFillInfoExpectation
|
||||
want *rpcScanner.ScanResponse
|
||||
wantErr string
|
||||
}{
|
||||
@@ -82,8 +82,8 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
fillInfoExpectation: vulnerability.FillInfoExpectation{
|
||||
Args: vulnerability.FillInfoArgs{
|
||||
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
|
||||
Args: vulnerability.OperationFillInfoArgs{
|
||||
Vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
|
||||
@@ -2,27 +2,32 @@
|
||||
|
||||
package vulnerability
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import pkgtypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
import types "github.com/aquasecurity/trivy/pkg/types"
|
||||
import (
|
||||
context "context"
|
||||
|
||||
pkgtypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
// MockOperation is an autogenerated mock type for the Operation type
|
||||
type MockOperation struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type FillInfoArgs struct {
|
||||
type OperationFillInfoArgs struct {
|
||||
Vulns []types.DetectedVulnerability
|
||||
VulnsAnything bool
|
||||
ReportType string
|
||||
ReportTypeAnything bool
|
||||
}
|
||||
|
||||
type FillInfoExpectation struct {
|
||||
Args FillInfoArgs
|
||||
type OperationFillInfoExpectation struct {
|
||||
Args OperationFillInfoArgs
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFillInfoExpectation(e FillInfoExpectation) {
|
||||
func (_m *MockOperation) ApplyFillInfoExpectation(e OperationFillInfoExpectation) {
|
||||
var args []interface{}
|
||||
if e.Args.VulnsAnything {
|
||||
args = append(args, mock.Anything)
|
||||
@@ -37,7 +42,7 @@ func (_m *MockOperation) ApplyFillInfoExpectation(e FillInfoExpectation) {
|
||||
_m.On("FillInfo", args...)
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFillInfoExpectations(expectations []FillInfoExpectation) {
|
||||
func (_m *MockOperation) ApplyFillInfoExpectations(expectations []OperationFillInfoExpectation) {
|
||||
for _, e := range expectations {
|
||||
_m.ApplyFillInfoExpectation(e)
|
||||
}
|
||||
@@ -48,7 +53,9 @@ func (_m *MockOperation) FillInfo(vulns []types.DetectedVulnerability, reportTyp
|
||||
_m.Called(vulns, reportType)
|
||||
}
|
||||
|
||||
type FilterArgs struct {
|
||||
type OperationFilterArgs struct {
|
||||
Ctx context.Context
|
||||
CtxAnything bool
|
||||
Vulns []types.DetectedVulnerability
|
||||
VulnsAnything bool
|
||||
Severities []pkgtypes.Severity
|
||||
@@ -57,19 +64,27 @@ type FilterArgs struct {
|
||||
IgnoreUnfixedAnything bool
|
||||
IgnoreFile string
|
||||
IgnoreFileAnything bool
|
||||
Policy string
|
||||
PolicyAnything bool
|
||||
}
|
||||
|
||||
type FilterReturns struct {
|
||||
type OperationFilterReturns struct {
|
||||
_a0 []types.DetectedVulnerability
|
||||
_a1 error
|
||||
}
|
||||
|
||||
type FilterExpectation struct {
|
||||
Args FilterArgs
|
||||
Returns FilterReturns
|
||||
type OperationFilterExpectation struct {
|
||||
Args OperationFilterArgs
|
||||
Returns OperationFilterReturns
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFilterExpectation(e FilterExpectation) {
|
||||
func (_m *MockOperation) ApplyFilterExpectation(e OperationFilterExpectation) {
|
||||
var args []interface{}
|
||||
if e.Args.CtxAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Ctx)
|
||||
}
|
||||
if e.Args.VulnsAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
@@ -90,27 +105,39 @@ func (_m *MockOperation) ApplyFilterExpectation(e FilterExpectation) {
|
||||
} else {
|
||||
args = append(args, e.Args.IgnoreFile)
|
||||
}
|
||||
_m.On("Filter", args...).Return(e.Returns._a0)
|
||||
if e.Args.PolicyAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Policy)
|
||||
}
|
||||
_m.On("Filter", args...).Return(e.Returns._a0, e.Returns._a1)
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFilterExpectations(expectations []FilterExpectation) {
|
||||
func (_m *MockOperation) ApplyFilterExpectations(expectations []OperationFilterExpectation) {
|
||||
for _, e := range expectations {
|
||||
_m.ApplyFilterExpectation(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter provides a mock function with given fields: vulns, severities, ignoreUnfixed, ignoreFile
|
||||
func (_m *MockOperation) Filter(vulns []types.DetectedVulnerability, severities []pkgtypes.Severity, ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability {
|
||||
ret := _m.Called(vulns, severities, ignoreUnfixed, ignoreFile)
|
||||
// Filter provides a mock function with given fields: ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy
|
||||
func (_m *MockOperation) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []pkgtypes.Severity, ignoreUnfixed bool, ignoreFile string, policy string) ([]types.DetectedVulnerability, error) {
|
||||
ret := _m.Called(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
|
||||
var r0 []types.DetectedVulnerability
|
||||
if rf, ok := ret.Get(0).(func([]types.DetectedVulnerability, []pkgtypes.Severity, bool, string) []types.DetectedVulnerability); ok {
|
||||
r0 = rf(vulns, severities, ignoreUnfixed, ignoreFile)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) []types.DetectedVulnerability); ok {
|
||||
r0 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]types.DetectedVulnerability)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) error); ok {
|
||||
r1 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
67
pkg/vulnerability/module.go
Normal file
67
pkg/vulnerability/module.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package vulnerability
|
||||
|
||||
const (
|
||||
module = `
|
||||
package lib.trivy
|
||||
|
||||
parse_cvss_vector_v3(cvss) = vector {
|
||||
s := split(cvss, "/")
|
||||
vector := {
|
||||
"AttackVector": attack_vector[s[1]],
|
||||
"AttackComplexity": attack_complexity[s[2]],
|
||||
"PrivilegesRequired": privileges_required[s[3]],
|
||||
"UserInteraction": user_interaction[s[4]],
|
||||
"Scope": scope[s[5]],
|
||||
"Confidentiality": confidentiality[s[6]],
|
||||
"Integrity": integrity[s[7]],
|
||||
"Availability": availability[s[8]],
|
||||
}
|
||||
}
|
||||
|
||||
attack_vector := {
|
||||
"AV:N": "Network",
|
||||
"AV:A": "Adjacent",
|
||||
"AV:L": "Local",
|
||||
"AV:P": "Physical",
|
||||
}
|
||||
|
||||
attack_complexity := {
|
||||
"AC:L": "Low",
|
||||
"AC:H": "High",
|
||||
}
|
||||
|
||||
privileges_required := {
|
||||
"PR:N": "None",
|
||||
"PR:L": "Low",
|
||||
"PR:H": "High",
|
||||
}
|
||||
|
||||
user_interaction := {
|
||||
"UI:N": "None",
|
||||
"UI:R": "Required",
|
||||
}
|
||||
|
||||
scope := {
|
||||
"S:U": "Unchanged",
|
||||
"S:C": "Changed",
|
||||
}
|
||||
|
||||
confidentiality := {
|
||||
"C:N": "None",
|
||||
"C:L": "Low",
|
||||
"C:H": "High",
|
||||
}
|
||||
|
||||
integrity := {
|
||||
"I:N": "None",
|
||||
"I:L": "Low",
|
||||
"I:H": "High",
|
||||
}
|
||||
|
||||
availability := {
|
||||
"A:N": "None",
|
||||
"A:L": "Low",
|
||||
"A:H": "High",
|
||||
}
|
||||
`
|
||||
)
|
||||
5
pkg/vulnerability/testdata/test.rego
vendored
Normal file
5
pkg/vulnerability/testdata/test.rego
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package trivy
|
||||
|
||||
ignore {
|
||||
input.VulnerabilityID != "CVE-2019-0001"
|
||||
}
|
||||
@@ -2,17 +2,19 @@ package vulnerability
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
|
||||
"github.com/google/wire"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
@@ -30,8 +32,8 @@ var SuperSet = wire.NewSet(
|
||||
|
||||
type Operation interface {
|
||||
FillInfo(vulns []types.DetectedVulnerability, reportType string)
|
||||
Filter(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability
|
||||
Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string, policy string) ([]types.DetectedVulnerability, error)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
@@ -89,8 +91,8 @@ func (c Client) getVendorSeverity(vuln *types.DetectedVulnerability, reportType
|
||||
}
|
||||
}
|
||||
|
||||
func (c Client) Filter(vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string) []types.DetectedVulnerability {
|
||||
func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string, policyFile string) ([]types.DetectedVulnerability, error) {
|
||||
ignoredIDs := getIgnoredIDs(ignoreFile)
|
||||
var vulnerabilities []types.DetectedVulnerability
|
||||
for _, vuln := range vulns {
|
||||
@@ -108,6 +110,15 @@ func (c Client) Filter(vulns []types.DetectedVulnerability, severities []dbTypes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if policyFile != "" {
|
||||
var err error
|
||||
vulnerabilities, err = applyPolicy(ctx, vulnerabilities, policyFile)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to apply the policy: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(vulnerabilities, func(i, j int) bool {
|
||||
if vulnerabilities[i].PkgName != vulnerabilities[j].PkgName {
|
||||
return vulnerabilities[i].PkgName < vulnerabilities[j].PkgName
|
||||
@@ -120,7 +131,45 @@ func (c Client) Filter(vulns []types.DetectedVulnerability, severities []dbTypes
|
||||
}
|
||||
return vulnerabilities[i].VulnerabilityID < vulnerabilities[j].VulnerabilityID
|
||||
})
|
||||
return vulnerabilities
|
||||
return vulnerabilities, nil
|
||||
}
|
||||
|
||||
func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, policyFile string) ([]types.DetectedVulnerability, error) {
|
||||
policy, err := ioutil.ReadFile(policyFile)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to read the policy file: %w", err)
|
||||
}
|
||||
|
||||
query, err := rego.New(
|
||||
rego.Query("data.trivy.ignore"),
|
||||
rego.Module("lib.rego", module),
|
||||
rego.Module("trivy.rego", string(policy)),
|
||||
).PrepareForEval(ctx)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to prepare for eval: %w", err)
|
||||
}
|
||||
|
||||
var filtered []types.DetectedVulnerability
|
||||
for _, vuln := range vulns {
|
||||
results, err := query.Eval(ctx, rego.EvalInput(vuln))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to evaluate the policy: %w", err)
|
||||
} else if len(results) == 0 {
|
||||
// Handle undefined result.
|
||||
filtered = append(filtered, vuln)
|
||||
continue
|
||||
}
|
||||
ignore, ok := results[0].Expressions[0].Value.(bool)
|
||||
if !ok {
|
||||
// Handle unexpected result type.
|
||||
return nil, xerrors.New("the policy must return boolean")
|
||||
}
|
||||
if ignore {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, vuln)
|
||||
}
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
func getIgnoredIDs(ignoreFile string) []string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package vulnerability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
)
|
||||
@@ -391,6 +393,7 @@ func TestClient_Filter(t *testing.T) {
|
||||
severities []dbTypes.Severity
|
||||
ignoreUnfixed bool
|
||||
ignoreFile string
|
||||
policyFile string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -566,11 +569,63 @@ func TestClient_Filter(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with a policy file",
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// this vulnerability is ignored
|
||||
VulnerabilityID: "CVE-2019-0002",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
// this vulnerability is ignored
|
||||
VulnerabilityID: "CVE-2019-0003",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
ignoreUnfixed: false,
|
||||
policyFile: "./testdata/test.rego",
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := Client{}
|
||||
got := c.Filter(tt.args.vulns, tt.args.severities, tt.args.ignoreUnfixed, tt.args.ignoreFile)
|
||||
got, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities,
|
||||
tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got, tt.name)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user