diff --git a/docs/docs/supply-chain/vex.md b/docs/docs/supply-chain/vex.md index a0b46fbbc1f0..a551629943cc 100644 --- a/docs/docs/supply-chain/vex.md +++ b/docs/docs/supply-chain/vex.md @@ -5,10 +5,11 @@ Trivy supports filtering detected vulnerabilities using [the Vulnerability Exploitability Exchange (VEX)](https://www.ntia.gov/files/ntia/publications/vex_one-page_summary.pdf), a standardized format for sharing and exchanging information about vulnerabilities. By providing VEX alongside the Software Bill of Materials (SBOM) during scanning, it is possible to filter vulnerabilities based on their status. -Currently, Trivy supports the following two formats: +Currently, Trivy supports the following three formats: - [CycloneDX](https://cyclonedx.org/capabilities/vex/) - [OpenVEX](https://github.com/openvex/spec) +- [CSAF](https://oasis-open.github.io/csaf-documentation/specification.html) This is still an experimental implementation, with only minimal functionality added. @@ -182,4 +183,139 @@ Total: 80 (UNKNOWN: 0, LOW: 58, MEDIUM: 6, HIGH: 16, CRITICAL: 0) CVE-2019-8457 is no longer shown as it is filtered out according to the given OpenVEX document. [openvex]: https://github.com/openvex/spec -[purl]: https://github.com/package-url/purl-spec \ No newline at end of file +[purl]: https://github.com/package-url/purl-spec + +## CSAF +Trivy also supports [CSAF][csaf] format for VEX. +Since CSAF aims to be SBOM format agnostic, both CycloneDX and SPDX formats are available for use as input SBOMs in Trivy. + +The following steps are required: + +1. Generate a SBOM (CycloneDX or SPDX) +2. Create a CSAF document based on the SBOM generated in step 1 +3. Provide the CSAF document when scanning the SBOM + +### Generating the SBOM +You can generate a CycloneDX or SPDX SBOM with Trivy as follows: + +```shell +$ trivy image --format spdx-json --output debian11.spdx.json debian:11 +``` + +### Create the CSAF document +Create a CSAF document in JSON format as follows: + +``` +$ cat < debian11.vex.csaf +{ + "document": { + "category": "csaf_vex", + "csaf_version": "2.0", + "notes": [ + { + "category": "summary", + "text": "Example Company VEX document. Unofficial content for demonstration purposes only.", + "title": "Author comment" + } + ], + "publisher": { + "category": "vendor", + "name": "Example Company ProductCERT", + "namespace": "https://psirt.example.com" + }, + "title": "AquaSecurity example VEX document", + "tracking": { + "current_release_date": "2024-01-01T11:00:00.000Z", + "generator": { + "date": "2024-01-01T11:00:00.000Z", + "engine": { + "name": "Secvisogram", + "version": "1.11.0" + } + }, + "id": "2024-EVD-UC-01-A-001", + "initial_release_date": "2024-01-01T11:00:00.000Z", + "revision_history": [ + { + "date": "2024-01-01T11:00:00.000Z", + "number": "1", + "summary": "Initial version." + } + ], + "status": "final", + "version": "1" + } + }, + "product_tree": { + "branches": [ + { + "branches": [ + { + "branches": [ + { + "category": "product_version", + "name": "5.3", + "product": { + "name": "Database Libraries 5.3", + "product_id": "LIBDB-5328", + "product_identification_helper": { + "purl": "pkg:deb/debian/libdb5.3@5.3.28%2Bdfsg1-0.8?arch=amd64\u0026distro=debian-11.8" + } + } + } + ], + "category": "product_name", + "name": "Database Libraries" + } + ], + "category": "vendor", + "name": "Debian" + } + ] + }, + "vulnerabilities": [ + { + "cve": "CVE-2019-8457", + "notes": [ + { + "category": "description", + "text": "SQLite3 from 3.6.0 to and including 3.27.2 is vulnerable to heap out-of-bound read in the rtreenode() function when handling invalid rtree tables.", + "title": "CVE description" + } + ], + "product_status": { + "known_not_affected": [ + "LIBDB-5328" + ] + }, + "threats": [ + { + "category": "impact", + "details": "Vulnerable code not in execute path.", + "product_ids": [ + "LIBDB-5328" + ] + } + ] + } + ] +} +EOF +``` + +### Scan SBOM with CSAF document +Provide the CSAF document when scanning the SBOM. + +```console +$ trivy sbom debian11.spdx.json --vex debian11.vex.csaf +... +2024-01-02T10:28:26.704+0100 INFO Filtered out the detected vulnerability {"VEX format": "CSAF", "vulnerability-id": "CVE-2019-8457", "status": "not_affected"} + +debian11.spdx.json (debian 11.6) +================================ +Total: 80 (UNKNOWN: 0, LOW: 58, MEDIUM: 6, HIGH: 16, CRITICAL: 0) +``` + +CVE-2019-8457 is no longer shown as it is filtered out according to the given CSAF document. + +[csaf]: https://oasis-open.github.io/csaf-documentation/specification.html diff --git a/go.mod b/go.mod index ba284038c23f..d85faa5eaf6c 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/cheggaaa/pb/v3 v3.1.4 github.com/containerd/containerd v1.7.11 + github.com/csaf-poc/csaf_distribution/v3 v3.0.0 github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 github.com/fatih/color v1.15.0 @@ -101,11 +102,11 @@ require ( github.com/twitchtv/twirp v8.1.2+incompatible github.com/xeipuuv/gojsonschema v1.2.0 github.com/xlab/treeprint v1.2.0 - go.etcd.io/bbolt v1.3.7 + go.etcd.io/bbolt v1.3.8 go.uber.org/zap v1.26.0 - golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/mod v0.14.0 - golang.org/x/sync v0.4.0 + golang.org/x/sync v0.5.0 golang.org/x/term v0.15.0 golang.org/x/text v0.14.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 @@ -131,11 +132,13 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.29 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect + github.com/Intevation/gval v1.3.0 // indirect + github.com/Intevation/jsonpath v0.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect @@ -144,7 +147,7 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect @@ -208,7 +211,7 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.6 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/continuity v0.4.2 // indirect github.com/containerd/fifo v1.1.0 // indirect @@ -252,29 +255,34 @@ require ( github.com/go-openapi/spec v0.20.9 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/validate v0.22.1 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/go-test/deep v1.1.0 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/goccy/go-yaml v1.8.1 // indirect + github.com/goccy/go-yaml v1.9.5 // indirect github.com/gofrs/uuid v4.3.1+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/btree v1.1.2 // indirect + github.com/google/flatbuffers v2.0.8+incompatible // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect github.com/google/s2a-go v0.1.5 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gosuri/uitable v0.0.4 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/huandu/xstrings v1.4.0 // indirect @@ -302,7 +310,7 @@ require ( github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032 // indirect - github.com/miekg/dns v1.1.50 // indirect + github.com/miekg/dns v1.1.53 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -342,6 +350,7 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/rubenv/sql-migrate v1.5.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -375,7 +384,7 @@ require ( golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/api v0.138.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect @@ -405,7 +414,7 @@ require ( modernc.org/memory v1.7.2 // indirect modernc.org/opt v0.1.3 // indirect modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.1 // indirect + modernc.org/token v1.1.0 // indirect oras.land/oras-go v1.2.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect diff --git a/go.sum b/go.sum index aeba5c1a5a92..ddba197f208d 100644 --- a/go.sum +++ b/go.sum @@ -216,8 +216,9 @@ github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/ github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= +github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -243,6 +244,10 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0= github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible h1:juIaKLLVhqzP55d8x4cSVgwyQv76Z55/fRv/UBr2KkQ= github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs= +github.com/Intevation/gval v1.3.0 h1:+Ze5sft5MmGbZrHj06NVUbcxCb67l9RaPTLMNr37mjw= +github.com/Intevation/gval v1.3.0/go.mod h1:xmGyGpP5be12EL0P12h+dqiYG8qn2j3PJxIgkoOHO5o= +github.com/Intevation/jsonpath v0.2.1 h1:rINNQJ0Pts5XTFEG+zamtdL7l9uuE1z0FBA+r55Sw+A= +github.com/Intevation/jsonpath v0.2.1/go.mod h1:WnZ8weMmwAx/fAO3SutjYFU+v7DFreNYnibV7CiaYIw= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -285,8 +290,8 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -567,8 +572,9 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= +github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -707,6 +713,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/csaf-poc/csaf_distribution/v3 v3.0.0 h1:ob9+Fmpff0YWgTP3dYaw7G2hKQ9cegh9l3zksc+q3sM= +github.com/csaf-poc/csaf_distribution/v3 v3.0.0/go.mod h1:uilCTiNKivq+6zrDvjtZaUeLk70oe21iwKivo6ILwlQ= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -798,6 +806,7 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -815,6 +824,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -894,21 +905,26 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= +github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= @@ -941,8 +957,9 @@ github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXs github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= 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/goccy/go-yaml v1.8.1 h1:JuZRFlqLM5cWF6A+waL8AKVuCcqvKOuhJtUQI+L3ez0= github.com/goccy/go-yaml v1.8.1/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y= +github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= +github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -1017,8 +1034,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= -github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -1069,8 +1086,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ= +github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg= github.com/google/s2a-go v0.1.5/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= @@ -1119,8 +1136,9 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -1163,8 +1181,8 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -1319,6 +1337,7 @@ github.com/masahiro331/go-xfs-filesystem v0.0.0-20230608043311-a335f4599b70/go.m github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -1348,8 +1367,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032 h1:TLygBUBxikNJJfLwgm+Qwdgq1FtfV8Uh7bcxRyTzK8s= github.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032/go.mod h1:vYT9HE7WCvL64iVeZylKmCsWKfE+JZ8105iuh2Trk8g= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= +github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -1578,6 +1597,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/saracen/walker v0.1.3 h1:YtcKKmpRPy6XJTHJ75J2QYXXZYWnZNQxPCVqZSHVV/g= github.com/saracen/walker v0.1.3/go.mod h1:FU+7qU8DeQQgSZDmmThMJi93kPkLFgy0oVAcLxurjIk= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -1667,6 +1688,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -1760,8 +1782,8 @@ github.com/zclconf/go-cty-yaml v1.0.3/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JApr go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= @@ -1852,8 +1874,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1934,7 +1956,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1997,8 +2018,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2223,11 +2244,10 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= 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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2487,7 +2507,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss= gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -2605,8 +2624,8 @@ modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= oras.land/oras-go v1.2.4-0.20230801060855-932dd06d38af h1:FX1C64cT+tNHJpuaHqbu48DEUp8gqtu6eBXtoP7CPyM= diff --git a/pkg/vex/csaf.go b/pkg/vex/csaf.go new file mode 100644 index 000000000000..efed27b83f22 --- /dev/null +++ b/pkg/vex/csaf.go @@ -0,0 +1,78 @@ +package vex + +import ( + csaf "github.com/csaf-poc/csaf_distribution/v3/csaf" + "github.com/samber/lo" + "go.uber.org/zap" + "golang.org/x/exp/slices" + + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/types" +) + +type CSAF struct { + advisory csaf.Advisory + logger *zap.SugaredLogger +} + +func newCSAF(advisory csaf.Advisory) VEX { + return &CSAF{ + advisory: advisory, + logger: log.Logger.With(zap.String("VEX format", "CSAF")), + } +} + +func (v *CSAF) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability { + return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool { + found, ok := lo.Find(v.advisory.Vulnerabilities, func(item *csaf.Vulnerability) bool { + return string(*item.CVE) == vuln.VulnerabilityID + }) + if !ok { + return true + } + + return v.affected(found, vuln.PkgIdentifier.PURL) + }) +} + +func (v *CSAF) affected(vuln *csaf.Vulnerability, purl *ftypes.PackageURL) bool { + if purl == nil || vuln.ProductStatus == nil { + return true + } + + var status Status + switch { + case v.matchPURL(purl, vuln.ProductStatus.KnownNotAffected): + status = StatusNotAffected + case v.matchPURL(purl, vuln.ProductStatus.Fixed): + status = StatusFixed + } + + if status != "" { + v.logger.Infow("Filtered out the detected vulnerability", + zap.String("vulnerability-id", string(*vuln.CVE)), + zap.String("status", string(status))) + return false + } + + return true +} + +// matchPURL returns true if the given PackageURL is found in the ProductTree. +func (v *CSAF) matchPURL(purl *ftypes.PackageURL, products *csaf.Products) bool { + for _, product := range lo.FromPtr(products) { + helpers := v.advisory.ProductTree.CollectProductIdentificationHelpers(lo.FromPtr(product)) + purls := lo.FilterMap(helpers, func(helper *csaf.ProductIdentificationHelper, _ int) (string, bool) { + if helper == nil || helper.PURL == nil { + return "", false + } + return string(*helper.PURL), true + }) + if slices.Contains(purls, purl.String()) { + return true + } + } + + return false +} diff --git a/pkg/vex/cyclonedx.go b/pkg/vex/cyclonedx.go new file mode 100644 index 000000000000..dbe65b5820a8 --- /dev/null +++ b/pkg/vex/cyclonedx.go @@ -0,0 +1,94 @@ +package vex + +import ( + cdx "github.com/CycloneDX/cyclonedx-go" + "github.com/samber/lo" + "go.uber.org/zap" + + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/types" +) + +type CycloneDX struct { + sbom *ftypes.CycloneDX + statements []Statement + logger *zap.SugaredLogger +} + +type Statement struct { + VulnerabilityID string + Affects []string + Status Status + Justification string // TODO: define a type +} + +func newCycloneDX(cdxSBOM *ftypes.CycloneDX, vex *cdx.BOM) *CycloneDX { + var stmts []Statement + for _, vuln := range lo.FromPtr(vex.Vulnerabilities) { + affects := lo.Map(lo.FromPtr(vuln.Affects), func(item cdx.Affects, index int) string { + return item.Ref + }) + + analysis := lo.FromPtr(vuln.Analysis) + stmts = append(stmts, Statement{ + VulnerabilityID: vuln.ID, + Affects: affects, + Status: cdxStatus(analysis.State), + Justification: string(analysis.Justification), + }) + } + return &CycloneDX{ + sbom: cdxSBOM, + statements: stmts, + logger: log.Logger.With(zap.String("VEX format", "CycloneDX")), + } +} + +func (v *CycloneDX) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability { + return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool { + stmt, ok := lo.Find(v.statements, func(item Statement) bool { + return item.VulnerabilityID == vuln.VulnerabilityID + }) + if !ok { + return true + } + return v.affected(vuln, stmt) + }) +} + +func (v *CycloneDX) affected(vuln types.DetectedVulnerability, stmt Statement) bool { + for _, affect := range stmt.Affects { + // Affect must be BOM-Link at the moment + link, err := cdx.ParseBOMLink(affect) + if err != nil { + v.logger.Warnw("Unable to parse BOM-Link", zap.String("affect", affect)) + continue + } + if v.sbom.SerialNumber != link.SerialNumber() || v.sbom.Version != link.Version() { + v.logger.Warnw("URN doesn't match with SBOM", zap.String("serial number", link.SerialNumber()), + zap.Int("version", link.Version())) + continue + } + if vuln.PkgIdentifier.Match(link.Reference()) && (stmt.Status == StatusNotAffected || stmt.Status == StatusFixed) { + v.logger.Infow("Filtered out the detected vulnerability", zap.String("vulnerability-id", vuln.VulnerabilityID), + zap.String("status", string(stmt.Status)), zap.String("justification", stmt.Justification)) + return false + } + } + return true +} + +func cdxStatus(s cdx.ImpactAnalysisState) Status { + switch s { + case cdx.IASResolved, cdx.IASResolvedWithPedigree: + return StatusFixed + case cdx.IASExploitable: + return StatusAffected + case cdx.IASInTriage: + return StatusUnderInvestigation + case cdx.IASFalsePositive, cdx.IASNotAffected: + return StatusNotAffected + } + return StatusUnknown +} diff --git a/pkg/vex/openvex.go b/pkg/vex/openvex.go new file mode 100644 index 000000000000..796439291b95 --- /dev/null +++ b/pkg/vex/openvex.go @@ -0,0 +1,46 @@ +package vex + +import ( + openvex "github.com/openvex/go-vex/pkg/vex" + "github.com/samber/lo" + "go.uber.org/zap" + + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/types" +) + +type OpenVEX struct { + vex openvex.VEX + logger *zap.SugaredLogger +} + +func newOpenVEX(vex openvex.VEX) VEX { + return &OpenVEX{ + vex: vex, + logger: log.Logger.With(zap.String("VEX format", "OpenVEX")), + } +} + +func (v *OpenVEX) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability { + return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool { + var stmts []openvex.Statement + if vuln.PkgIdentifier.PURL != nil { + matchedStmts := v.vex.Matches(vuln.VulnerabilityID, vuln.PkgIdentifier.PURL.String(), nil) + stmts = append(stmts, matchedStmts...) + } + if len(stmts) == 0 { + return true + } + + // Take the latest statement for a given vulnerability and product + // as a sequence of statements can be overridden by the newer one. + // cf. https://github.com/openvex/spec/blob/fa5ba0c0afedb008dc5ebad418548cacf16a3ca7/OPENVEX-SPEC.md#the-vex-statement + stmt := stmts[len(stmts)-1] + if stmt.Status == openvex.StatusNotAffected || stmt.Status == openvex.StatusFixed { + v.logger.Infow("Filtered out the detected vulnerability", zap.String("vulnerability-id", vuln.VulnerabilityID), + zap.String("status", string(stmt.Status)), zap.String("justification", string(stmt.Justification))) + return false + } + return true + }) +} diff --git a/pkg/vex/testdata/csaf-affected.json b/pkg/vex/testdata/csaf-affected.json new file mode 100644 index 000000000000..56e9bb4d8a53 --- /dev/null +++ b/pkg/vex/testdata/csaf-affected.json @@ -0,0 +1,93 @@ +{ + "document": { + "category": "csaf_vex", + "csaf_version": "2.0", + "notes": [ + { + "category": "summary", + "text": "Example Company VEX document. Unofficial content for demonstration purposes only.", + "title": "Author comment" + } + ], + "publisher": { + "category": "vendor", + "name": "Example Company ProductCERT", + "namespace": "https://psirt.example.com" + }, + "title": "Example VEX Document Use Case 1 - Affected", + "tracking": { + "current_release_date": "2022-03-03T11:00:00.000Z", + "generator": { + "date": "2022-03-03T11:00:00.000Z", + "engine": { + "name": "Secvisogram", + "version": "1.11.0" + } + }, + "id": "2022-EVD-UC-01-A-001", + "initial_release_date": "2022-03-03T11:00:00.000Z", + "revision_history": [ + { + "date": "2022-03-03T11:00:00.000Z", + "number": "1", + "summary": "Initial version." + } + ], + "status": "final", + "version": "1" + } + }, + "product_tree": { + "branches": [ + { + "branches": [ + { + "branches": [ + { + "category": "product_version", + "name": "1.0", + "product": { + "name": "Example Company DEF 1.0", + "product_id": "CSAFPID-0001", + "product_identification_helper": { + "purl": "pkg:maven/org.example.company/def@1.0" + } + } + } + ], + "category": "product_name", + "name": "DEF" + } + ], + "category": "vendor", + "name": "Example Company" + } + ] + }, + "vulnerabilities": [ + { + "cve": "CVE-2021-44228", + "notes": [ + { + "category": "description", + "text": "Apache Log4j2 2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and 2.3.1) JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled. From log4j 2.15.0, this behavior has been disabled by default. From version 2.16.0 (along with 2.12.2, 2.12.3, and 2.3.1), this functionality has been completely removed. Note that this vulnerability is specific to log4j-core and does not affect log4net, log4cxx, or other Apache Logging Services projects.", + "title": "CVE description" + } + ], + "product_status": { + "known_affected": [ + "CSAFPID-0001" + ] + }, + "remediations": [ + { + "category": "vendor_fix", + "details": "Customers should update to version 1.1 of product DEF which fixes the issue.", + "product_ids": [ + "CSAFPID-0001" + ] + } + ] + } + ] +} diff --git a/pkg/vex/testdata/csaf-not-affected.json b/pkg/vex/testdata/csaf-not-affected.json new file mode 100644 index 000000000000..dce0b4a712d6 --- /dev/null +++ b/pkg/vex/testdata/csaf-not-affected.json @@ -0,0 +1,93 @@ +{ + "document": { + "category": "csaf_vex", + "csaf_version": "2.0", + "notes": [ + { + "category": "summary", + "text": "Example Company VEX document. Unofficial content for demonstration purposes only.", + "title": "Author comment" + } + ], + "publisher": { + "category": "vendor", + "name": "Example Company ProductCERT", + "namespace": "https://psirt.example.com" + }, + "title": "AquaSecurity example VEX document", + "tracking": { + "current_release_date": "2022-03-03T11:00:00.000Z", + "generator": { + "date": "2022-03-03T11:00:00.000Z", + "engine": { + "name": "Secvisogram", + "version": "1.11.0" + } + }, + "id": "2022-EVD-UC-01-A-001", + "initial_release_date": "2022-03-03T11:00:00.000Z", + "revision_history": [ + { + "date": "2022-03-03T11:00:00.000Z", + "number": "1", + "summary": "Initial version." + } + ], + "status": "final", + "version": "1" + } + }, + "product_tree": { + "branches": [ + { + "branches": [ + { + "branches": [ + { + "category": "product_version", + "name": "2.6.0", + "product": { + "name": "Spring Boot 2.6.0", + "product_id": "SPB-00260", + "product_identification_helper": { + "purl": "pkg:maven/org.springframework.boot/spring-boot@2.6.0" + } + } + } + ], + "category": "product_name", + "name": "Spring Boot" + } + ], + "category": "vendor", + "name": "Spring" + } + ] + }, + "vulnerabilities": [ + { + "cve": "CVE-2021-44228", + "notes": [ + { + "category": "description", + "text": "Apache Log4j2 2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and 2.3.1) JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled. From log4j 2.15.0, this behavior has been disabled by default. From version 2.16.0 (along with 2.12.2, 2.12.3, and 2.3.1), this functionality has been completely removed. Note that this vulnerability is specific to log4j-core and does not affect log4net, log4cxx, or other Apache Logging Services projects.", + "title": "CVE description" + } + ], + "product_status": { + "known_not_affected": [ + "SPB-00260" + ] + }, + "threats": [ + { + "category": "impact", + "details": "Class with vulnerable code was removed before shipping.", + "product_ids": [ + "SPB-00260" + ] + } + ] + } + ] +} diff --git a/pkg/vex/testdata/unknown.json b/pkg/vex/testdata/unknown.json index e0415f90563c..9e26dfeeb6e6 100644 --- a/pkg/vex/testdata/unknown.json +++ b/pkg/vex/testdata/unknown.json @@ -1 +1 @@ -{unknown} \ No newline at end of file +{} \ No newline at end of file diff --git a/pkg/vex/vex.go b/pkg/vex/vex.go index 1042e0886515..644b00d62b3a 100644 --- a/pkg/vex/vex.go +++ b/pkg/vex/vex.go @@ -5,16 +5,12 @@ import ( "io" "os" - cdx "github.com/CycloneDX/cyclonedx-go" + csaf "github.com/csaf-poc/csaf_distribution/v3/csaf" "github.com/hashicorp/go-multierror" openvex "github.com/openvex/go-vex/pkg/vex" - "github.com/samber/lo" "github.com/sirupsen/logrus" - "go.uber.org/zap" "golang.org/x/xerrors" - ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/sbom" "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx" "github.com/aquasecurity/trivy/pkg/types" @@ -27,130 +23,6 @@ type VEX interface { Filter([]types.DetectedVulnerability) []types.DetectedVulnerability } -type Statement struct { - VulnerabilityID string - Affects []string - Status Status - Justification string // TODO: define a type -} - -type OpenVEX struct { - vex openvex.VEX - logger *zap.SugaredLogger -} - -func newOpenVEX(vex openvex.VEX) VEX { - logger := log.Logger.With(zap.String("VEX format", "OpenVEX")) - - return &OpenVEX{ - vex: vex, - logger: logger, - } -} - -func (v *OpenVEX) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability { - return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool { - var stmts []openvex.Statement - if vuln.PkgIdentifier.PURL != nil { - matchedStmts := v.vex.Matches(vuln.VulnerabilityID, vuln.PkgIdentifier.PURL.String(), nil) - if len(matchedStmts) > 0 { - stmts = append(stmts, matchedStmts...) - } - } - if len(stmts) == 0 { - return true - } - - // Take the latest statement for a given vulnerability and product - // as a sequence of statements can be overridden by the newer one. - // cf. https://github.com/openvex/spec/blob/fa5ba0c0afedb008dc5ebad418548cacf16a3ca7/OPENVEX-SPEC.md#the-vex-statement - stmt := stmts[len(stmts)-1] - if stmt.Status == openvex.StatusNotAffected || stmt.Status == openvex.StatusFixed { - v.logger.Infow("Filtered out the detected vulnerability", zap.String("vulnerability-id", vuln.VulnerabilityID), - zap.String("status", string(stmt.Status)), zap.String("justification", string(stmt.Justification))) - return false - } - return true - }) -} - -type CycloneDX struct { - sbom *ftypes.CycloneDX - statements []Statement - logger *zap.SugaredLogger -} - -func newCycloneDX(cdxSBOM *ftypes.CycloneDX, vex *cdx.BOM) *CycloneDX { - var stmts []Statement - for _, vuln := range lo.FromPtr(vex.Vulnerabilities) { - affects := lo.Map(lo.FromPtr(vuln.Affects), func(item cdx.Affects, index int) string { - return item.Ref - }) - - analysis := lo.FromPtr(vuln.Analysis) - - stmts = append(stmts, Statement{ - VulnerabilityID: vuln.ID, - Affects: affects, - Status: cdxStatus(analysis.State), - Justification: string(analysis.Justification), - }) - } - return &CycloneDX{ - sbom: cdxSBOM, - statements: stmts, - logger: log.Logger.With(zap.String("VEX format", "CycloneDX")), - } -} - -func (v *CycloneDX) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability { - return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool { - stmt, ok := lo.Find(v.statements, func(item Statement) bool { - return item.VulnerabilityID == vuln.VulnerabilityID - }) - if !ok { - return true - } - return v.affected(vuln, stmt) - }) -} - -func (v *CycloneDX) affected(vuln types.DetectedVulnerability, stmt Statement) bool { - for _, affect := range stmt.Affects { - // Affect must be BOM-Link at the moment - link, err := cdx.ParseBOMLink(affect) - if err != nil { - v.logger.Warnw("Unable to parse BOM-Link", zap.String("affect", affect)) - continue - } - if v.sbom.SerialNumber != link.SerialNumber() || v.sbom.Version != link.Version() { - v.logger.Warnw("URN doesn't match with SBOM", zap.String("serial number", link.SerialNumber()), - zap.Int("version", link.Version())) - continue - } - if vuln.PkgIdentifier.Match(link.Reference()) && (stmt.Status == StatusNotAffected || stmt.Status == StatusFixed) { - v.logger.Infow("Filtered out the detected vulnerability", zap.String("vulnerability-id", vuln.VulnerabilityID), - zap.String("status", string(stmt.Status)), zap.String("justification", stmt.Justification)) - return false - } - } - return true -} - -func cdxStatus(s cdx.ImpactAnalysisState) Status { - switch s { - case cdx.IASResolved, cdx.IASResolvedWithPedigree: - return StatusFixed - case cdx.IASExploitable: - return StatusAffected - case cdx.IASInTriage: - return StatusUnderInvestigation - case cdx.IASFalsePositive, cdx.IASNotAffected: - return StatusNotAffected - } - return StatusUnknown -} - func New(filePath string, report types.Report) (VEX, error) { if filePath == "" { return nil, nil @@ -162,7 +34,6 @@ func New(filePath string, report types.Report) (VEX, error) { defer f.Close() var errs error - // Try CycloneDX JSON if ok, err := sbom.IsCycloneDXJSON(f); err != nil { errs = multierror.Append(errs, err) @@ -173,7 +44,14 @@ func New(filePath string, report types.Report) (VEX, error) { // Try OpenVEX if v, err := decodeOpenVEX(f); err != nil { errs = multierror.Append(errs, err) - } else { + } else if v != nil { + return v, nil + } + + // Try CSAF + if v, err := decodeCSAF(f); err != nil { + errs = multierror.Append(errs, err) + } else if v != nil { return v, nil } @@ -210,3 +88,17 @@ func decodeOpenVEX(r io.ReadSeeker) (VEX, error) { } return newOpenVEX(openVEX), nil } + +func decodeCSAF(r io.ReadSeeker) (VEX, error) { + if _, err := r.Seek(0, io.SeekStart); err != nil { + return nil, xerrors.Errorf("seek error: %w", err) + } + var adv csaf.Advisory + if err := json.NewDecoder(r).Decode(&adv); err != nil { + return nil, err + } + if adv.Vulnerabilities == nil { + return nil, nil + } + return newCSAF(adv), nil +} diff --git a/pkg/vex/vex_test.go b/pkg/vex/vex_test.go index 16abcf2a7ffa..1feb37a7c41d 100644 --- a/pkg/vex/vex_test.go +++ b/pkg/vex/vex_test.go @@ -1,10 +1,11 @@ package vex_test import ( - "github.com/package-url/packageurl-go" "os" "testing" + "github.com/package-url/packageurl-go" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -28,10 +29,11 @@ func TestVEX_Filter(t *testing.T) { vulns []types.DetectedVulnerability } tests := []struct { - name string - fields fields - args args - want []types.DetectedVulnerability + name string + fields fields + args args + want []types.DetectedVulnerability + wantErr string }{ { name: "OpenVEX", @@ -227,11 +229,91 @@ func TestVEX_Filter(t *testing.T) { }, }, }, + { + name: "CSAF (not affected vuln)", + fields: fields{ + filePath: "testdata/csaf-not-affected.json", + }, + args: args{ + vulns: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2021-44228", + PkgName: "spring-boot", + InstalledVersion: "2.6.0", + PkgIdentifier: ftypes.PkgIdentifier{ + PURL: &ftypes.PackageURL{ + PackageURL: packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.springframework.boot", + Name: "spring-boot", + Version: "2.6.0", + }, + }, + }, + }, + }, + }, + want: []types.DetectedVulnerability{}, + }, + { + name: "CSAF (affected vuln)", + fields: fields{ + filePath: "testdata/csaf-affected.json", + }, + args: args{ + vulns: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2021-44228", + PkgName: "def", + InstalledVersion: "1.0", + PkgIdentifier: ftypes.PkgIdentifier{ + PURL: &ftypes.PackageURL{ + PackageURL: packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.example.company", + Name: "def", + Version: "1.0", + }, + }, + }, + }, + }, + }, + want: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2021-44228", + PkgName: "def", + InstalledVersion: "1.0", + PkgIdentifier: ftypes.PkgIdentifier{ + PURL: &ftypes.PackageURL{ + PackageURL: packageurl.PackageURL{ + Type: packageurl.TypeMaven, + Namespace: "org.example.company", + Name: "def", + Version: "1.0", + }, + }, + }, + }, + }, + }, + { + name: "unknown format", + fields: fields{ + filePath: "testdata/unknown.json", + }, + args: args{}, + wantErr: "unable to load VEX", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v, err := vex.New(tt.fields.filePath, tt.fields.report) + if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + return + } require.NoError(t, err) assert.Equal(t, tt.want, v.Filter(tt.args.vulns)) })