diff --git a/.chronicle.yaml b/.chronicle.yaml new file mode 100644 index 00000000000..400437c89ad --- /dev/null +++ b/.chronicle.yaml @@ -0,0 +1 @@ +enforce-v0: true # don't make breaking-change label bump major version before 1.0. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 90f8ed10fc2..f59426d4859 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6 + uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6 + uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 diff --git a/.github/workflows/oss-project-board-add.yaml b/.github/workflows/oss-project-board-add.yaml new file mode 100644 index 00000000000..b0d1fca007a --- /dev/null +++ b/.github/workflows/oss-project-board-add.yaml @@ -0,0 +1,16 @@ +name: Add to OSS board + +on: + issues: + types: + - opened + - reopened + - transferred + - labeled + +jobs: + + run: + uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main" + secrets: + token: ${{ secrets.OSS_PROJECT_GH_TOKEN }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9d928aca3cf..1294ce704c6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -142,7 +142,7 @@ jobs: AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }} - - uses: anchore/sbom-action@4d571ad1038a9cc29d676154ef265ab8f9027042 # v0.14.2 + - uses: anchore/sbom-action@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1 # v0.14.3 continue-on-error: true with: artifact-name: sbom.spdx.json diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index d9af6659462..7bac78bdee0 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -25,7 +25,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # tag=v2.1.3 + uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # tag=v2.2.0 with: results_file: results.sarif results_format: sarif @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@83f0fe6c4988d98a455712a27f0255212bba9bd4 # tag=v1.0.26 + uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # tag=v1.0.26 with: sarif_file: results.sarif diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index e829989a74c..8287c678ee6 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -58,7 +58,7 @@ jobs: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@284f54f989303d2699d373481a0cfa13ad5a6666 # v5.0.1 + - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2 with: signoff: true delete-branch: true diff --git a/.github/workflows/update-syft-release.yml b/.github/workflows/update-syft-release.yml index 1215cff1b5f..e051dd4144f 100644 --- a/.github/workflows/update-syft-release.yml +++ b/.github/workflows/update-syft-release.yml @@ -44,7 +44,7 @@ jobs: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@284f54f989303d2699d373481a0cfa13ad5a6666 # v5.0.1 + - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2 with: signoff: true delete-branch: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 37fecf92b59..a978d888d01 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,57 @@ If you are looking to contribute to this project and want to open a GitHub pull request ("PR"), there are a few guidelines of what we are looking for in patches. Make sure you go through this document and ensure that your code proposal is aligned. +## Setting up your environment + +Before you can contribute to Grype, you need to configure your development environment. + +### Debian setup + +You will need to install Go. The version on https://go.dev works best, using the system golang doesn't always work the way you might expect. + +Refer to the go.mod file in the root of this repo for the recommended version of Go to install. + +You will also need Docker. There's no reason the system packages shouldn't work, but we used the official Docker package. You can find instructions for installing Docker in Debian [here](https://docs.docker.com/engine/install/debian/). + +You also need to install some Debian packages + +```sh +sudo apt-get install build-essential git libxml2-utils +``` + +## Configuring Git + +You will need to configure your git client with your name and email address. This is easily done from the command line. + +```text +$ git config --global user.name "John Doe" +$ git config --global user.email "john.doe@example.com" +``` + +This username and email address will matter later in this guide. + +## Fork the repo + +You should fork the Grype repo using the "Fork" button at the top right of the Grype GitHub [site](https://github.com/anchore/grype/). You will be doing your development in your fork, then submit a pull request to Grype. There are many resources how to use GitHub effectively, we will not cover those here. + +## Adding a feature or fix + +If you look at the Grype [Issue](https://github.com/anchore/grype/issues) there are plenty of bugs and feature requests. Maybe look at the [good first issue](https://github.com/anchore/grype/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) list if you're not sure where to start. + +## Commit guidelines + +In the Grype project we like commits and pull requests (PR) to be easy to understand and review. Open source thrives best when everything happening is over documented and small enough to be understood. + +### Granular commits + +Please try to make every commit as simple as possible, but no simpler. The idea is that each commit should be a logical unit of code. Try not to commit too many tiny changes, for example every line changed in a file as a separate commit. And also try not to make a commit enormous, for example committing all your work at the end of the day. + +Rather than try to follow a strict guide on what is or is not best, we try to be flexible and simple in this space. Do what makes the most sense for the changes you are trying to include. + +### Commit title and description + +Remember that the message you leave for a commit is for the reviewer in the present, and for someone (maybe you) changing something in the future. Please make sure the title and description used is easy to understand and explains what was done. Jokes and clever comments generally don't age well in commit messages. Just the facts please. + ## Sign off your work The `sign-off` is an added line at the end of the explanation for the commit, certifying that you wrote it or otherwise have the right to submit it as an open-source patch. By submitting a contribution, you agree to be bound by the terms of the DCO Version 1.1 and Apache License Version 2.0. @@ -70,18 +121,6 @@ Date: Mon Aug 1 11:27:13 2020 -0400 Signed-off-by: John Doe ``` - -[//]: # (TODO: Commit guidelines, granular commits) - - -[//]: # (TODO: Commit guidelines, descriptive messages) - - -[//]: # (TODO: Commit guidelines, commit title, extra body description) - - -[//]: # (TODO: PR title and description) - ## Test your changes This project has a `Makefile` which includes many helpers running both unit and integration tests. Although PRs will have automatic checks for these, it is useful to run them locally, ensuring they pass before submitting changes. Ensure you've bootstrapped once before running tests: @@ -97,11 +136,30 @@ $ make unit $ make integration ``` +You can also run `make all` to run a more extensive test suite, but there is additional configuration that will be needed for those tests to run correctly. We will not cover the extra steps here. + +## Pull Request + +If you made it this far and all the tests are passing, it's time to submit a Pull Request (PR) for Grype. Submitting a PR is always a scary moment as what happens next can be an unknown. The Grype project strives to be easy to work with, we appreciate all contributions. Nobody is going to yell at you or try to make you feel bad. We love contributions and know how scary that first PR can be. + +### PR Title and Description + +Just like the commit title and description mentioned above, the PR title and description is very important for letting others know what's happening. Please include any details you think a reviewer will need to more properly review your PR. + +A PR that is very large or poorly described has a higher likelihood of being pushed to the end of the list. Reviewers like PRs they can understand and quickly review. + +### What to expect next + +Please be patient with the project. We try to review PRs in a timely manner, but this is highly dependent on all the other tasks we have going on. It's OK to ask for a status update every week or two, it's not OK to ask for a status update every day. + +It's very likely the reviewer will have questions and suggestions for changes to your PR. If your changes don't match the current style and flow of the other code, expect a request to change what you've done. + ## Document your changes -When proposed changes are modifying user-facing functionality or output, it is expected the PR will include updates to the documentation as well. +And lastly, when proposed changes are modifying user-facing functionality or output, it is expected the PR will include updates to the documentation as well. Grype is not a project that is heavy on documentation. This will mostly be updating the README and help for the tool. +If nobody knows new features exist, they can't use them! ## Security Vulnerabilities -Found a security vulnerability? See in our [Security Policy](SECURITY.md) to see how to report it to be solved as soon as possible. \ No newline at end of file +Found a security vulnerability? See in our [Security Policy](SECURITY.md) to see how to report it to be solved as soon as possible. diff --git a/DEVELOPING.md b/DEVELOPING.md index 5357f21caff..9f40c10abc4 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -4,8 +4,6 @@ There are a few useful things to know before diving into the codebase. This proj ## Getting started -### Native Development - After cloning do the following: 1. run `go build main.go` to get a binary named `main` from the source (use `-o ` to get a differently named binary), or optionally `go run main.go` to run from source. @@ -19,14 +17,6 @@ The main make tasks for common static analysis and testing are `lint`, `format`, See `make help` for all the current make tasks. -### Docker Development - -This depends on Docker and Docker Compose - -1. run `docker-compose build grype` to build the local development container -2. run `docker-compose run --rm grype bash` to enter into the container with all the bootstrapped dependencies installed. -3. run `make` to verify everything is installed and working properly - ## Relationship to Syft Grype uses Syft as a library for all-things related to obtaining and parsing the given scan target (pulling container diff --git a/Makefile b/Makefile index b6a49b6820e..9b7ced0d690 100644 --- a/Makefile +++ b/Makefile @@ -10,11 +10,11 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle GLOW_CMD = $(TEMP_DIR)/glow # Tool versions ################################# -GOLANGCILINT_VERSION := v1.53.2 +GOLANGCILINT_VERSION := v1.53.3 GOSIMPORTS_VERSION := v0.3.8 BOUNCER_VERSION := v0.4.0 CHRONICLE_VERSION := v0.6.0 -GORELEASER_VERSION := v1.18.2 +GORELEASER_VERSION := v1.19.2 YAJSV_VERSION := v1.4.1 QUILL_VERSION := v0.2.0 GLOW_VERSION := v1.5.1 diff --git a/README.md b/README.md index b3512974317..9c37ed988e7 100644 --- a/README.md +++ b/README.md @@ -267,20 +267,9 @@ Grype lets you define custom output formats, using [Go templates](https://golang - Grype's template processing uses the same data models as the `json` output format — so if you're wondering what data is available as you author a template, you can use the output from `grype -o json` as a reference. -**Example:** You could make Grype output data in CSV format by writing a Go template that renders CSV data and then running `grype -o template -t ~/path/to/csv.tmpl`. - **Please note:** Templates can access information about the system they are running on, such as environment variables. You should never run untrusted templates. -Here's what the `csv.tmpl` file might look like: - -```gotemplate -"Package","Version Installed","Vulnerability ID","Severity" -{{- range .Matches}} -"{{.Artifact.Name}}","{{.Artifact.Version}}","{{.Vulnerability.ID}}","{{.Vulnerability.Severity}}" -{{- end}} -``` - -Which would produce output like: +There are several example templates in the [templates](https://github.com/anchore/grype/tree/main/templates) directory in the Grype source which can serve a starting point for a custom output format. For example, [csv.tmpl](https://github.com/anchore/grype/blob/main/templates/csv.tmpl) produces a vulnerability report in CSV (comma separated value) format: ```text "Package","Version Installed","Vulnerability ID","Severity" @@ -290,6 +279,8 @@ Which would produce output like: ... ``` +You can also find the template for the default "table" output format in the same place. + Grype also includes a vast array of utility templating functions from [sprig](http://masterminds.github.io/sprig/) apart from the default golang [text/template](https://pkg.go.dev/text/template#hdr-Functions) to allow users to customize the output from Grype. ### Gating on severity of vulnerabilities diff --git a/go.mod b/go.mod index ea6fa015cdf..fa9b1f6f057 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,9 @@ require ( github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20230522170632-e14bc4437b2e + github.com/anchore/stereoscope v0.0.0-20230627195312-cd49355d934e github.com/bmatcuk/doublestar/v2 v2.0.4 - github.com/docker/docker v24.0.2+incompatible + github.com/docker/docker v24.0.4+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/gabriel-vasile/mimetype v1.4.2 @@ -45,7 +45,7 @@ require ( github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb github.com/x-cray/logrus-prefixed-formatter v0.5.2 - golang.org/x/term v0.8.0 + golang.org/x/term v0.10.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/gorm v1.23.10 ) @@ -53,7 +53,7 @@ require ( require ( github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8 github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963 - github.com/anchore/syft v0.83.0 + github.com/anchore/syft v0.84.2-0.20230705174713-cfbb9f703bd7 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/termenv v0.15.1 @@ -153,7 +153,7 @@ require ( github.com/sassoftware/go-rpmutils v0.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/skeema/knownhosts v1.1.1 // indirect - github.com/spdx/tools-golang v0.5.1 // indirect + github.com/spdx/tools-golang v0.5.2 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/stretchr/objx v0.5.0 // indirect @@ -171,14 +171,14 @@ require ( github.com/zclconf/go-cty v1.10.0 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/goleak v1.2.0 // indirect - golang.org/x/crypto v0.9.0 // indirect + golang.org/x/crypto v0.10.0 // indirect golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.10.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.11.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.10.0 // indirect golang.org/x/time v0.2.0 // indirect golang.org/x/tools v0.8.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect @@ -197,7 +197,7 @@ require ( modernc.org/mathutil v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.23.0 // indirect + modernc.org/sqlite v1.23.1 // indirect modernc.org/strutil v1.1.3 // indirect modernc.org/token v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index c6965d86966..fe1d9f68e84 100644 --- a/go.sum +++ b/go.sum @@ -241,10 +241,10 @@ github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwM github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963 h1:vrf2PYH77vqVJoNR15ZuFJ63qwBMqrmGIt/7VsBhLF8= github.com/anchore/sqlite v1.4.6-0.20220607210448-bcc6ee5c4963/go.mod h1:AVRyXOUP0hTz9Cb8OlD1XnwA8t4lBPfTuwPHmEUuiLc= -github.com/anchore/stereoscope v0.0.0-20230522170632-e14bc4437b2e h1:YPWJxds1hKRedS92u7O6D6ULVOx1F2HGgS4CWqJdBYw= -github.com/anchore/stereoscope v0.0.0-20230522170632-e14bc4437b2e/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g= -github.com/anchore/syft v0.83.0 h1:6LupUjGux4kXnEdJ2kaC3fMero3vO2dIwqikUhNrnWY= -github.com/anchore/syft v0.83.0/go.mod h1:yKlcdgpP8VvINuVahtwklCHXI01kFlLmI7VkpDRaU+Y= +github.com/anchore/stereoscope v0.0.0-20230627195312-cd49355d934e h1:zhk3ZLtomMJ750nNCE+c24PonMzoO/SeL/4uTr1L9kM= +github.com/anchore/stereoscope v0.0.0-20230627195312-cd49355d934e/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g= +github.com/anchore/syft v0.84.2-0.20230705174713-cfbb9f703bd7 h1:E8pdc689HTwXaHLRcmMTGi6TBukDa6oD8dQ0bJTSUm0= +github.com/anchore/syft v0.84.2-0.20230705174713-cfbb9f703bd7/go.mod h1:4ruIUJNJY2IsuUPrvUdYu8kG4ScFjGoiy/PPmgBEuTw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= @@ -321,8 +321,8 @@ github.com/docker/cli v23.0.5+incompatible h1:ufWmAOuD3Vmr7JP2G5K3cyuNC4YZWiAsuD github.com/docker/cli v23.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg= -github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.4+incompatible h1:s/LVDftw9hjblvqIeTiGYXBCD95nOEEl7qRsRrIOuQI= +github.com/docker/docker v24.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -786,8 +786,8 @@ github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIn github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= -github.com/spdx/tools-golang v0.5.1 h1:fJg3SVOGG+eIva9ZUBm/hvyA7PIPVFjRxUKe6fdAgwE= -github.com/spdx/tools-golang v0.5.1/go.mod h1:/DRDQuBfB37HctM29YtrX1v+bXiVmT2OpQDalRmX9aU= +github.com/spdx/tools-golang v0.5.2 h1:dtMNjJreWPe37584ajk7m/rQtfJaLpRMk7pUGgvekOg= +github.com/spdx/tools-golang v0.5.2/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= @@ -906,8 +906,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -948,8 +948,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1010,8 +1010,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1155,8 +1155,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1165,8 +1165,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1180,8 +1180,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1557,8 +1557,8 @@ modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= -modernc.org/sqlite v1.23.0 h1:MWTFBI5H1WLnXpNBh/BTruBVqzzoh28DA0iOnlkkRaM= -modernc.org/sqlite v1.23.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= +modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= +modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= diff --git a/grype/distro/distro_test.go b/grype/distro/distro_test.go index 8b6cecdfc28..faa61825f3c 100644 --- a/grype/distro/distro_test.go +++ b/grype/distro/distro_test.go @@ -227,7 +227,7 @@ func Test_NewDistroFromRelease_Coverage(t *testing.T) { for _, test := range tests { t.Run(test.fixture, func(t *testing.T) { - s, err := source.NewFromDirectory(test.fixture) + s, err := source.NewFromDirectory(source.DirectoryConfig{Path: test.fixture}) require.NoError(t, err) resolver, err := s.FileResolver(source.SquashedScope) diff --git a/grype/match/explicit_ignores.go b/grype/match/explicit_ignores.go index 2c29a3f840c..0ec272b1a90 100644 --- a/grype/match/explicit_ignores.go +++ b/grype/match/explicit_ignores.go @@ -18,7 +18,7 @@ func init() { // https://github.com/mergebase/log4j-samples collection, we want to filter these explicitly: { typ: "java-archive", - vulnerabilities: []string{"CVE-2021-44228", "CVE-2021-45046", "GHSA-jfh8-c2jp-5v3q", "GHSA-7rjr-3q55-vv33"}, + vulnerabilities: []string{"CVE-2021-44228", "CVE-2021-45046", "GHSA-jfh8-c2jp-5v3q", "GHSA-7rjr-3q55-vv33", "CVE-2020-9493", "CVE-2022-23307", "CVE-2023-26464"}, packages: []string{"log4j-api", "log4j-slf4j-impl", "log4j-to-slf4j", "log4j-1.2-api", "log4j-detector", "log4j-over-slf4j", "slf4j-log4j12"}, }, // Based on https://github.com/anchore/grype/issues/558: @@ -69,7 +69,7 @@ func init() { } // ApplyExplicitIgnoreRules Filters out matches meeting the criteria defined above and those within the grype database -func ApplyExplicitIgnoreRules(provider ExclusionProvider, matches Matches) Matches { +func ApplyExplicitIgnoreRules(provider ExclusionProvider, matches Matches) (Matches, []IgnoredMatch) { var ignoreRules []IgnoreRule ignoreRules = append(ignoreRules, explicitIgnoreRules...) @@ -84,18 +84,5 @@ func ApplyExplicitIgnoreRules(provider ExclusionProvider, matches Matches) Match ignoreRules = append(ignoreRules, r...) } - matches, ignored := ApplyIgnoreRules(matches, ignoreRules) - - if len(ignored) > 0 { - log.Debugf("Removed %d explicit vulnerability matches:", len(ignored)) - for idx, i := range ignored { - branch := "├──" - if idx == len(ignored)-1 { - branch = "└──" - } - log.Debugf(" %s %s : %s", branch, i.Match.Vulnerability.ID, i.Package.PURL) - } - } - - return matches + return ApplyIgnoreRules(matches, ignoreRules) } diff --git a/grype/match/explicit_ignores_test.go b/grype/match/explicit_ignores_test.go index 79af5e7256c..c95930de7c4 100644 --- a/grype/match/explicit_ignores_test.go +++ b/grype/match/explicit_ignores_test.go @@ -39,6 +39,7 @@ func Test_ApplyExplicitIgnoreRules(t *testing.T) { typ syftPkg.Type matches []cvePkg expected []string + ignored []string }{ // some explicit log4j-related data: // "CVE-2021-44228", "CVE-2021-45046", "GHSA-jfh8-c2jp-5v3q", "GHSA-7rjr-3q55-vv33", @@ -69,6 +70,7 @@ func Test_ApplyExplicitIgnoreRules(t *testing.T) { {"CVE-2021-44228", "log4j-core"}, }, expected: []string{"log4j-core"}, + ignored: []string{"log4j-api"}, }, { name: "filters all matching CVEs and packages", @@ -78,6 +80,7 @@ func Test_ApplyExplicitIgnoreRules(t *testing.T) { {"GHSA-jfh8-c2jp-5v3q", "log4j-slf4j-impl"}, }, expected: []string{}, + ignored: []string{"log4j-api", "log4j-slf4j-impl"}, }, { name: "filters invalid CVEs for protobuf Go module", @@ -87,6 +90,7 @@ func Test_ApplyExplicitIgnoreRules(t *testing.T) { {"CVE-2021-22570", "google.golang.org/protobuf"}, }, expected: []string{}, + ignored: []string{"google.golang.org/protobuf", "google.golang.org/protobuf"}, }, { name: "keeps valid CVEs for protobuf Go module", @@ -118,7 +122,7 @@ func Test_ApplyExplicitIgnoreRules(t *testing.T) { }) } - filtered := ApplyExplicitIgnoreRules(p, matches) + filtered, ignores := ApplyExplicitIgnoreRules(p, matches) var found []string for match := range filtered.Enumerate() { @@ -126,6 +130,16 @@ func Test_ApplyExplicitIgnoreRules(t *testing.T) { } assert.ElementsMatch(t, test.expected, found) + + if len(test.ignored) > 0 { + var ignored []string + for _, i := range ignores { + ignored = append(ignored, i.Package.Name) + } + assert.ElementsMatch(t, test.ignored, ignored) + } else { + assert.Empty(t, ignores) + } }) } } diff --git a/grype/match/ignore_test.go b/grype/match/ignore_test.go index 4490bf8a736..57af7c53d20 100644 --- a/grype/match/ignore_test.go +++ b/grype/match/ignore_test.go @@ -9,8 +9,8 @@ import ( grypeDb "github.com/anchore/grype/grype/db/v5" "github.com/anchore/grype/grype/pkg" "github.com/anchore/grype/grype/vulnerability" + "github.com/anchore/syft/syft/file" syftPkg "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" ) var ( @@ -28,7 +28,7 @@ var ( Name: "dive", Version: "0.5.2", Type: "deb", - Locations: source.NewLocationSet(source.NewLocation("/path/that/has/dive")), + Locations: file.NewLocationSet(file.NewLocation("/path/that/has/dive")), }, }, { @@ -45,7 +45,7 @@ var ( Version: "100.0.50", Language: syftPkg.Ruby, Type: syftPkg.GemPkg, - Locations: source.NewLocationSet(source.NewVirtualLocation("/real/path/with/reach", + Locations: file.NewLocationSet(file.NewVirtualLocation("/real/path/with/reach", "/virtual/path/that/has/reach")), }, }, @@ -63,7 +63,7 @@ var ( Version: "100.0.51", Language: syftPkg.Ruby, Type: syftPkg.GemPkg, - Locations: source.NewLocationSet(source.NewVirtualLocation("/real/path/with/beach", + Locations: file.NewLocationSet(file.NewVirtualLocation("/real/path/with/beach", "/virtual/path/that/has/beach")), }, }, @@ -81,7 +81,7 @@ var ( Version: "100.0.52", Language: syftPkg.Ruby, Type: syftPkg.GemPkg, - Locations: source.NewLocationSet(source.NewVirtualLocation("/real/path/with/speach", + Locations: file.NewLocationSet(file.NewVirtualLocation("/real/path/with/speach", "/virtual/path/that/has/speach")), }, }, @@ -337,9 +337,9 @@ var ( ID: pkg.ID(uuid.NewString()), Name: "a-pkg", Version: "1.0", - Locations: source.NewLocationSet( - source.NewLocation("/some/path"), - source.NewVirtualLocation("/some/path", "/some/virtual/path"), + Locations: file.NewLocationSet( + file.NewLocation("/some/path"), + file.NewVirtualLocation("/some/path", "/some/virtual/path"), ), Type: "rpm", }, diff --git a/grype/matcher/matchers.go b/grype/matcher/matchers.go index a7e060fd875..6761bbdf986 100644 --- a/grype/matcher/matchers.go +++ b/grype/matcher/matchers.go @@ -147,6 +147,8 @@ func FindMatches(store interface { res := match.NewMatches() matcherIndex, defaultMatcher := newMatcherIndex(matchers) + var ignored []match.IgnoredMatch + var d *distro.Distro if release != nil { d, err = distro.NewFromRelease(*release) @@ -177,6 +179,10 @@ func FindMatches(store interface { if err != nil { log.Warnf("matcher failed for pkg=%s: %+v", p, err) } else { + // Filter out matches based on records in the database exclusion table and hard-coded rules + filtered, ignores := match.ApplyExplicitIgnoreRules(store, match.NewMatches(matches...)) + ignored = append(ignored, ignores...) + matches := filtered.Sorted() logMatches(p, matches) res.Add(matches...) progressMonitor.VulnerabilitiesDiscovered.Add(int64(len(matches))) @@ -189,8 +195,7 @@ func FindMatches(store interface { logListSummary(progressMonitor) - // Filter out matches based off of the records in the exclusion table in the database or from the old hard-coded rules - res = match.ApplyExplicitIgnoreRules(store, res) + logIgnoredMatches(ignored) return res } @@ -216,6 +221,19 @@ func logListSummary(vl *monitor) { } } +func logIgnoredMatches(ignored []match.IgnoredMatch) { + if len(ignored) > 0 { + log.Debugf("Removed %d explicit vulnerability matches:", len(ignored)) + for idx, i := range ignored { + branch := "├──" + if idx == len(ignored)-1 { + branch = "└──" + } + log.Debugf(" %s %s : %s", branch, i.Match.Vulnerability.ID, i.Package.PURL) + } + } +} + func updateVulnerabilityList(list *monitor, matches []match.Match, metadataProvider vulnerability.MetadataProvider) { for _, m := range matches { metadata, err := metadataProvider.GetMetadata(m.Vulnerability.ID, m.Vulnerability.Namespace) diff --git a/grype/pkg/context.go b/grype/pkg/context.go index 4a2e65b56c9..5f46a6f9f9c 100644 --- a/grype/pkg/context.go +++ b/grype/pkg/context.go @@ -6,6 +6,6 @@ import ( ) type Context struct { - Source *source.Metadata + Source *source.Description Distro *linux.Release } diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index 2e4a5703e08..b863d47ce35 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -14,7 +14,6 @@ import ( "github.com/anchore/syft/syft/file" syftFile "github.com/anchore/syft/syft/file" syftPkg "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" ) func TestNew(t *testing.T) { @@ -550,8 +549,8 @@ func TestFromCollection_DoesNotPanic(t *testing.T) { examplePackage := syftPkg.Package{ Name: "test", Version: "1.2.3", - Locations: source.NewLocationSet( - source.NewLocation("/test-path"), + Locations: file.NewLocationSet( + file.NewLocation("/test-path"), ), Type: syftPkg.NpmPkg, } diff --git a/grype/pkg/provider_test.go b/grype/pkg/provider_test.go index a9aaf1f1899..34dd94432c5 100644 --- a/grype/pkg/provider_test.go +++ b/grype/pkg/provider_test.go @@ -6,8 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg/cataloger" - "github.com/anchore/syft/syft/source" ) func TestProviderLocationExcludes(t *testing.T) { @@ -158,10 +158,10 @@ func Test_filterPackageExclusions(t *testing.T) { t.Run(test.name, func(t *testing.T) { var packages []Package for _, pkg := range test.locations { - locations := source.NewLocationSet() + locations := file.NewLocationSet() for _, l := range pkg { locations.Add( - source.NewVirtualLocation(l, l), + file.NewVirtualLocation(l, l), ) } packages = append(packages, Package{Locations: locations}) @@ -221,7 +221,7 @@ func Test_matchesLocation(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - matches, err := locationMatches(source.NewVirtualLocation(test.realPath, test.virtualPath), test.match) + matches, err := locationMatches(file.NewVirtualLocation(test.realPath, test.virtualPath), test.match) assert.NoError(t, err) assert.Equal(t, test.expected, matches) }) diff --git a/grype/pkg/syft_provider.go b/grype/pkg/syft_provider.go index 8b541ba2a60..8094f9794b5 100644 --- a/grype/pkg/syft_provider.go +++ b/grype/pkg/syft_provider.go @@ -1,26 +1,19 @@ package pkg import ( + "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) func syftProvider(userInput string, config ProviderConfig) ([]Package, Context, *sbom.SBOM, error) { - if config.CatalogingOptions.Search.Scope == "" { - return nil, Context{}, nil, errDoesNotProvide - } - - sourceInput, err := source.ParseInputWithName(userInput, config.Platform, config.Name, config.DefaultImagePullSource) + src, err := getSource(userInput, config) if err != nil { return nil, Context{}, nil, err } - src, cleanup, err := source.New(*sourceInput, config.RegistryOptions, config.Exclusions) - if err != nil { - return nil, Context{}, nil, err - } - defer cleanup() + defer src.Close() catalog, relationships, theDistro, err := syft.CatalogPackages(src, config.CatalogingOptions) if err != nil { @@ -29,14 +22,16 @@ func syftProvider(userInput string, config ProviderConfig) ([]Package, Context, catalog = removePackagesByOverlap(catalog, relationships) + srcDescription := src.Describe() + packages := FromCollection(catalog, config.SynthesisConfig) context := Context{ - Source: &src.Metadata, + Source: &srcDescription, Distro: theDistro, } sbom := &sbom.SBOM{ - Source: src.Metadata, + Source: srcDescription, Relationships: relationships, Artifacts: sbom.Artifacts{ Packages: catalog, @@ -45,3 +40,35 @@ func syftProvider(userInput string, config ProviderConfig) ([]Package, Context, return packages, context, sbom, nil } + +func getSource(userInput string, config ProviderConfig) (source.Source, error) { + if config.CatalogingOptions.Search.Scope == "" { + return nil, errDoesNotProvide + } + + detection, err := source.Detect(userInput, source.DetectConfig{ + DefaultImageSource: config.DefaultImagePullSource, + }) + if err != nil { + return nil, err + } + + var platform *image.Platform + if config.Platform != "" { + platform, err = image.NewPlatform(config.Platform) + if err != nil { + return nil, err + } + } + + return detection.NewSource(source.DetectionSourceConfig{ + Alias: source.Alias{ + Name: config.Name, + }, + RegistryOptions: config.RegistryOptions, + Platform: platform, + Exclude: source.ExcludeConfig{ + Paths: config.Exclusions, + }, + }) +} diff --git a/grype/pkg/syft_sbom_provider_test.go b/grype/pkg/syft_sbom_provider_test.go index 774dda2a874..4d1b0a12164 100644 --- a/grype/pkg/syft_sbom_provider_test.go +++ b/grype/pkg/syft_sbom_provider_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/source" ) @@ -26,8 +27,8 @@ func TestParseSyftJSON(t *testing.T) { { Name: "alpine-baselayout", Version: "3.2.0-r6", - Locations: source.NewLocationSet( - source.NewLocationFromCoordinates(source.Coordinates{ + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates(file.Coordinates{ RealPath: "/lib/apk/db/installed", FileSystemID: "sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759", }), @@ -50,8 +51,8 @@ func TestParseSyftJSON(t *testing.T) { { Name: "fake", Version: "1.2.0", - Locations: source.NewLocationSet( - source.NewLocationFromCoordinates(source.Coordinates{ + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates(file.Coordinates{ RealPath: "/lib/apk/db/installed", FileSystemID: "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c", }), @@ -76,8 +77,8 @@ func TestParseSyftJSON(t *testing.T) { { Name: "gmp", Version: "6.2.0-r0", - Locations: source.NewLocationSet( - source.NewLocationFromCoordinates(source.Coordinates{ + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates(file.Coordinates{ RealPath: "/lib/apk/db/installed", FileSystemID: "sha256:93cf4cfb673c7e16a9e74f731d6767b70b92a0b7c9f59d06efd72fbff535371c", }), @@ -101,11 +102,10 @@ func TestParseSyftJSON(t *testing.T) { }, }, Context: Context{ - Source: &source.Metadata{ - Scheme: source.ImageScheme, - ImageMetadata: source.ImageMetadata{ + Source: &source.Description{ + Metadata: source.StereoscopeImageSourceMetadata{ UserInput: "alpine:fake", - Layers: []source.LayerMetadata{ + Layers: []source.StereoscopeLayerMetadata{ { MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", Digest: "sha256:50644c29ef5a27c9a40c393a73ece2479de78325cae7d762ef3cdc19bf42dd0a", @@ -120,7 +120,6 @@ func TestParseSyftJSON(t *testing.T) { "alpine:fake", }, }, - Path: "", }, Distro: &linux.Release{ Name: "alpine", @@ -138,8 +137,12 @@ func TestParseSyftJSON(t *testing.T) { t.Fatalf("unable to parse: %+v", err) } - context.Source.ImageMetadata.RawConfig = nil - context.Source.ImageMetadata.RawManifest = nil + if m, ok := context.Source.Metadata.(source.StereoscopeImageSourceMetadata); ok { + m.RawConfig = nil + m.RawManifest = nil + + context.Source.Metadata = m + } for _, d := range deep.Equal(test.Packages, pkgs) { if strings.Contains(d, ".ID: ") { @@ -179,8 +182,8 @@ var springImageTestCase = struct { { Name: "charsets", Version: "", - Locations: source.NewLocationSet( - source.NewLocationFromCoordinates(source.Coordinates{ + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates(file.Coordinates{ RealPath: "/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar", FileSystemID: "sha256:a1a6ceadb701ab4e6c93b243dc2a0daedc8cee23a24203845ecccd5784cd1393", }), @@ -199,8 +202,8 @@ var springImageTestCase = struct { { Name: "tomcat-embed-el", Version: "9.0.27", - Locations: source.NewLocationSet( - source.NewLocationFromCoordinates(source.Coordinates{ + Locations: file.NewLocationSet( + file.NewLocationFromCoordinates(file.Coordinates{ RealPath: "/app/libs/tomcat-embed-el-9.0.27.jar", FileSystemID: "sha256:89504f083d3f15322f97ae240df44650203f24427860db1b3d32e66dd05940e4", }), @@ -218,11 +221,10 @@ var springImageTestCase = struct { }, }, Context: Context{ - Source: &source.Metadata{ - Scheme: source.ImageScheme, - ImageMetadata: source.ImageMetadata{ + Source: &source.Description{ + Metadata: source.StereoscopeImageSourceMetadata{ UserInput: "springio/gs-spring-boot-docker:latest", - Layers: []source.LayerMetadata{ + Layers: []source.StereoscopeLayerMetadata{ { MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", Digest: "sha256:42a3027eaac150d2b8f516100921f4bd83b3dbc20bfe64124f686c072b49c602", @@ -238,7 +240,6 @@ var springImageTestCase = struct { }, RepoDigests: []string{"springio/gs-spring-boot-docker@sha256:39c2ffc784f5f34862e22c1f2ccdbcb62430736114c13f60111eabdb79decb08"}, }, - Path: "", }, Distro: &linux.Release{ Name: "debian", diff --git a/grype/presenter/cyclonedx/presenter.go b/grype/presenter/cyclonedx/presenter.go index 1069d9106a1..54b1a957b3f 100644 --- a/grype/presenter/cyclonedx/presenter.go +++ b/grype/presenter/cyclonedx/presenter.go @@ -20,7 +20,7 @@ import ( type Presenter struct { results match.Matches packages []pkg.Package - srcMetadata *source.Metadata + src *source.Description metadataProvider vulnerability.MetadataProvider format cyclonedx.BOMFileFormat sbom *sbom.SBOM @@ -32,7 +32,7 @@ func NewJSONPresenter(pb models.PresenterConfig) *Presenter { results: pb.Matches, packages: pb.Packages, metadataProvider: pb.MetadataProvider, - srcMetadata: pb.Context.Source, + src: pb.Context.Source, sbom: pb.SBOM, format: cyclonedx.BOMFileFormatJSON, } @@ -44,7 +44,7 @@ func NewXMLPresenter(pb models.PresenterConfig) *Presenter { results: pb.Matches, packages: pb.Packages, metadataProvider: pb.MetadataProvider, - srcMetadata: pb.Context.Source, + src: pb.Context.Source, sbom: pb.SBOM, format: cyclonedx.BOMFileFormatXML, } diff --git a/grype/presenter/cyclonedx/presenter_test.go b/grype/presenter/cyclonedx/presenter_test.go index bd45a9cc888..c0e4debb900 100644 --- a/grype/presenter/cyclonedx/presenter_test.go +++ b/grype/presenter/cyclonedx/presenter_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/go-testutils" + "github.com/anchore/grype/grype/presenter/internal" "github.com/anchore/grype/grype/presenter/models" - "github.com/anchore/syft/syft/source" ) var update = flag.Bool("update", false, "update the *.golden files for cyclonedx presenters") @@ -17,8 +17,8 @@ var update = flag.Bool("update", false, "update the *.golden files for cyclonedx func TestCycloneDxPresenterImage(t *testing.T) { var buffer bytes.Buffer - matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme) - sbom := models.SBOMFromPackages(t, packages) + matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.ImageSource) + sbom := internal.SBOMFromPackages(t, packages) pb := models.PresenterConfig{ Matches: matches, Packages: packages, @@ -42,16 +42,16 @@ func TestCycloneDxPresenterImage(t *testing.T) { var expected = testutils.GetGoldenFileContents(t) // remove dynamic values, which are tested independently - actual = models.Redact(actual) - expected = models.Redact(expected) + actual = internal.Redact(actual) + expected = internal.Redact(expected) require.JSONEq(t, string(expected), string(actual)) } func TestCycloneDxPresenterDir(t *testing.T) { var buffer bytes.Buffer - matches, packages, ctx, metadataProvider, _, _ := models.GenerateAnalysis(t, source.DirectoryScheme) - sbom := models.SBOMFromPackages(t, packages) + matches, packages, ctx, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.DirectorySource) + sbom := internal.SBOMFromPackages(t, packages) pb := models.PresenterConfig{ Matches: matches, Packages: packages, @@ -76,8 +76,8 @@ func TestCycloneDxPresenterDir(t *testing.T) { var expected = testutils.GetGoldenFileContents(t) // remove dynamic values, which are tested independently - actual = models.Redact(actual) - expected = models.Redact(expected) + actual = internal.Redact(actual) + expected = internal.Redact(expected) require.JSONEq(t, string(expected), string(actual)) } diff --git a/grype/presenter/models/models_helpers.go b/grype/presenter/internal/test_helpers.go similarity index 70% rename from grype/presenter/models/models_helpers.go rename to grype/presenter/internal/test_helpers.go index d8a745a83c2..c1471822b77 100644 --- a/grype/presenter/models/models_helpers.go +++ b/grype/presenter/internal/test_helpers.go @@ -1,4 +1,4 @@ -package models +package internal import ( "regexp" @@ -9,6 +9,7 @@ import ( grypeDb "github.com/anchore/grype/grype/db/v5" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/grype/grype/vulnerability" "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/syft/syft/artifact" @@ -20,17 +21,25 @@ import ( syftSource "github.com/anchore/syft/syft/source" ) -func GenerateAnalysis(t *testing.T, scheme syftSource.Scheme) (match.Matches, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) { +const ( + DirectorySource SyftSource = "directory" + ImageSource SyftSource = "image" + FileSource SyftSource = "file" +) + +type SyftSource string + +func GenerateAnalysis(t *testing.T, scheme SyftSource) (match.Matches, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) { t.Helper() packages := generatePackages(t) matches := generateMatches(t, packages[0], packages[1]) context := generateContext(t, scheme) - return matches, packages, context, NewMetadataMock(), nil, nil + return matches, packages, context, models.NewMetadataMock(), nil, nil } -func GenerateAnalysisWithIgnoredMatches(t *testing.T, scheme syftSource.Scheme) (match.Matches, []match.IgnoredMatch, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) { +func GenerateAnalysisWithIgnoredMatches(t *testing.T, scheme SyftSource) (match.Matches, []match.IgnoredMatch, []pkg.Package, pkg.Context, vulnerability.MetadataProvider, interface{}, interface{}) { t.Helper() packages := generatePackages(t) @@ -38,7 +47,7 @@ func GenerateAnalysisWithIgnoredMatches(t *testing.T, scheme syftSource.Scheme) ignoredMatches := generateIgnoredMatches(t, packages[1]) context := generateContext(t, scheme) - return matches, ignoredMatches, packages, context, NewMetadataMock(), nil, nil + return matches, ignoredMatches, packages, context, models.NewMetadataMock(), nil, nil } func SBOMFromPackages(t *testing.T, packages []pkg.Package) *sbom.SBOM { @@ -260,59 +269,84 @@ func generatePackages(t *testing.T) []pkg.Package { return updatedPkgs } -func generateContext(t *testing.T, scheme syftSource.Scheme) pkg.Context { - var src syftSource.Source - img := image.Image{ - Metadata: image.Metadata{ - ID: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f", - ManifestDigest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5", - MediaType: "application/vnd.docker.distribution.manifest.v2+json", - Size: 65, - }, - Layers: []*image.Layer{ - { - Metadata: image.LayerMetadata{ - Digest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5", - MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", - Size: 22, - }, +//nolint:funlen +func generateContext(t *testing.T, scheme SyftSource) pkg.Context { + var ( + src syftSource.Source + desc syftSource.Description + ) + + switch scheme { + case FileSource: + var err error + src, err = syftSource.NewFromFile(syftSource.FileConfig{ + Path: "user-input", + }) + if err != nil { + t.Fatalf("failed to generate mock file source from mock image: %+v", err) + } + desc = src.Describe() + case ImageSource: + img := image.Image{ + Metadata: image.Metadata{ + ID: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f", + ManifestDigest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5", + MediaType: "application/vnd.docker.distribution.manifest.v2+json", + Size: 65, }, - { - Metadata: image.LayerMetadata{ - Digest: "sha256:a05cd9ebf88af96450f1e25367281ab232ac0645f314124fe01af759b93f3006", - MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", - Size: 16, + Layers: []*image.Layer{ + { + Metadata: image.LayerMetadata{ + Digest: "sha256:ca738abb87a8d58f112d3400ebb079b61ceae7dc290beb34bda735be4b1941d5", + MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", + Size: 22, + }, }, - }, - { - Metadata: image.LayerMetadata{ - Digest: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f", - MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", - Size: 27, + { + Metadata: image.LayerMetadata{ + Digest: "sha256:a05cd9ebf88af96450f1e25367281ab232ac0645f314124fe01af759b93f3006", + MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", + Size: 16, + }, + }, + { + Metadata: image.LayerMetadata{ + Digest: "sha256:ab5608d634db2716a297adbfa6a5dd5d8f8f5a7d0cab73649ea7fbb8c8da544f", + MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", + Size: 27, + }, }, }, - }, - } + } - switch scheme { - case syftSource.ImageScheme: var err error - src, err = syftSource.NewFromImage(&img, "user-input") + src, err = syftSource.NewFromStereoscopeImageObject(&img, "user-input", nil) if err != nil { t.Fatalf("failed to generate mock image source from mock image: %+v", err) } - case syftSource.DirectoryScheme: + desc = src.Describe() + case DirectorySource: + // note: the dir must exist for the source to be created + d := t.TempDir() var err error - src, err = syftSource.NewFromDirectory("/some/path") + src, err = syftSource.NewFromDirectory(syftSource.DirectoryConfig{ + Path: d, + }) + if err != nil { t.Fatalf("failed to generate mock directory source from mock dir: %+v", err) } + desc = src.Describe() + if m, ok := desc.Metadata.(syftSource.DirectorySourceMetadata); ok { + m.Path = "/some/path" + desc.Metadata = m + } default: t.Fatalf("unknown scheme: %s", scheme) } return pkg.Context{ - Source: &src.Metadata, + Source: &desc, Distro: &linux.Release{ Name: "centos", IDLike: []string{ diff --git a/grype/presenter/json/presenter_test.go b/grype/presenter/json/presenter_test.go index 12a471c1fc1..7806ba60ef1 100644 --- a/grype/presenter/json/presenter_test.go +++ b/grype/presenter/json/presenter_test.go @@ -11,6 +11,7 @@ import ( "github.com/anchore/go-testutils" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/presenter/internal" "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/source" @@ -21,7 +22,7 @@ var timestampRegexp = regexp.MustCompile(`"timestamp":\s*"[^"]+"`) func TestJsonImgsPresenter(t *testing.T) { var buffer bytes.Buffer - matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme) + matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.ImageSource) pb := models.PresenterConfig{ Matches: matches, @@ -54,7 +55,7 @@ func TestJsonImgsPresenter(t *testing.T) { func TestJsonDirsPresenter(t *testing.T) { var buffer bytes.Buffer - matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, source.DirectoryScheme) + matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.DirectorySource) pb := models.PresenterConfig{ Matches: matches, @@ -91,7 +92,7 @@ func TestEmptyJsonPresenter(t *testing.T) { matches := match.NewMatches() ctx := pkg.Context{ - Source: &source.Metadata{}, + Source: &source.Description{}, Distro: &linux.Release{ ID: "centos", IDLike: []string{"rhel"}, diff --git a/grype/presenter/models/document_test.go b/grype/presenter/models/document_test.go index dc0031f4881..588a074cf89 100644 --- a/grype/presenter/models/document_test.go +++ b/grype/presenter/models/document_test.go @@ -72,9 +72,8 @@ func TestPackagesAreSorted(t *testing.T) { packages := []pkg.Package{pkg1, pkg2} ctx := pkg.Context{ - Source: &syftSource.Metadata{ - Scheme: syftSource.DirectoryScheme, - ImageMetadata: syftSource.ImageMetadata{}, + Source: &syftSource.Description{ + Metadata: syftSource.DirectorySourceMetadata{}, }, Distro: &linux.Release{ ID: "centos", diff --git a/grype/presenter/models/source.go b/grype/presenter/models/source.go index 8648770c40d..bdfecbe3c52 100644 --- a/grype/presenter/models/source.go +++ b/grype/presenter/models/source.go @@ -12,39 +12,38 @@ type source struct { } // newSource creates a new source object to be represented into JSON. -func newSource(src syftSource.Metadata) (source, error) { - switch src.Scheme { - case syftSource.ImageScheme: - metadata := src.ImageMetadata +func newSource(src syftSource.Description) (source, error) { + switch m := src.Metadata.(type) { + case syftSource.StereoscopeImageSourceMetadata: // ensure that empty collections are not shown as null - if metadata.RepoDigests == nil { - metadata.RepoDigests = []string{} + if m.RepoDigests == nil { + m.RepoDigests = []string{} } - if metadata.Tags == nil { - metadata.Tags = []string{} + if m.Tags == nil { + m.Tags = []string{} } return source{ Type: "image", - Target: metadata, + Target: m, }, nil - case syftSource.DirectoryScheme: + case syftSource.DirectorySourceMetadata: return source{ Type: "directory", - Target: src.Path, + Target: m.Path, }, nil - case syftSource.FileScheme: + case syftSource.FileSourceMetadata: return source{ Type: "file", - Target: src.Path, + Target: m.Path, }, nil - case "": + case nil: // we may be showing results from a input source that does not support source information return source{ Type: "unknown", Target: "unknown", }, nil default: - return source{}, fmt.Errorf("unsupported source: %q", src.Scheme) + return source{}, fmt.Errorf("unsupported source: %T", src.Metadata) } } diff --git a/grype/presenter/models/source_test.go b/grype/presenter/models/source_test.go index b1e33a5b132..fc24bef2b26 100644 --- a/grype/presenter/models/source_test.go +++ b/grype/presenter/models/source_test.go @@ -12,14 +12,13 @@ import ( func TestNewSource(t *testing.T) { testCases := []struct { name string - metadata syftSource.Metadata + metadata syftSource.Description expected source }{ { name: "image", - metadata: syftSource.Metadata{ - Scheme: syftSource.ImageScheme, - ImageMetadata: syftSource.ImageMetadata{ + metadata: syftSource.Description{ + Metadata: syftSource.StereoscopeImageSourceMetadata{ UserInput: "abc", ID: "def", ManifestDigest: "abcdef", @@ -28,7 +27,7 @@ func TestNewSource(t *testing.T) { }, expected: source{ Type: "image", - Target: syftSource.ImageMetadata{ + Target: syftSource.StereoscopeImageSourceMetadata{ UserInput: "abc", ID: "def", ManifestDigest: "abcdef", @@ -40,9 +39,10 @@ func TestNewSource(t *testing.T) { }, { name: "directory", - metadata: syftSource.Metadata{ - Scheme: syftSource.DirectoryScheme, - Path: "/foo/bar", + metadata: syftSource.Description{ + Metadata: syftSource.DirectorySourceMetadata{ + Path: "/foo/bar", + }, }, expected: source{ Type: "directory", @@ -51,9 +51,10 @@ func TestNewSource(t *testing.T) { }, { name: "file", - metadata: syftSource.Metadata{ - Scheme: syftSource.FileScheme, - Path: "/foo/bar/test.zip", + metadata: syftSource.Description{ + Metadata: syftSource.FileSourceMetadata{ + Path: "/foo/bar/test.zip", + }, }, expected: source{ Type: "file", @@ -62,18 +63,12 @@ func TestNewSource(t *testing.T) { }, } - var testedSchemes []syftSource.Scheme - for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { actual, err := newSource(testCase.metadata) require.NoError(t, err) assert.Equal(t, testCase.expected, actual) - testedSchemes = append(testedSchemes, testCase.metadata.Scheme) }) } - - // Ensure we have test coverage for all possible syftSource.Scheme values. - assert.ElementsMatchf(t, syftSource.AllSchemes, testedSchemes, "not all scheme values are being tested") } diff --git a/grype/presenter/sarif/presenter.go b/grype/presenter/sarif/presenter.go index 63a899df3a3..95eb7c2ff15 100644 --- a/grype/presenter/sarif/presenter.go +++ b/grype/presenter/sarif/presenter.go @@ -22,7 +22,7 @@ import ( type Presenter struct { results match.Matches packages []pkg.Package - srcMetadata *source.Metadata + src *source.Description metadataProvider vulnerability.MetadataProvider } @@ -32,7 +32,7 @@ func NewPresenter(pb models.PresenterConfig) *Presenter { results: pb.Matches, packages: pb.Packages, metadataProvider: pb.MetadataProvider, - srcMetadata: pb.Context.Source, + src: pb.Context.Source, } } @@ -163,10 +163,19 @@ func (pres *Presenter) packagePath(p pkg.Package) string { // inputPath returns a friendlier relative path or absolute path depending on the input, not prefixed by . or ./ func (pres *Presenter) inputPath() string { - if pres.srcMetadata == nil { + if pres.src == nil { return "" } - inputPath := strings.TrimPrefix(pres.srcMetadata.Path, "./") + var inputPath string + switch m := pres.src.Metadata.(type) { + case source.FileSourceMetadata: + inputPath = m.Path + case source.DirectorySourceMetadata: + inputPath = m.Path + default: + return "" + } + inputPath = strings.TrimPrefix(inputPath, "./") if inputPath == "." { return "" } @@ -182,13 +191,17 @@ func (pres *Presenter) locationPath(l file.Location) string { in := pres.inputPath() path = strings.TrimPrefix(path, "./") // trimmed off any ./ and accounted for dir:. for both path and input path - if pres.srcMetadata != nil && pres.srcMetadata.Scheme == source.DirectoryScheme { - if filepath.IsAbs(path) || in == "" { - return path + if pres.src != nil { + _, ok := pres.src.Metadata.(source.DirectorySourceMetadata) + if ok { + if filepath.IsAbs(path) || in == "" { + return path + } + // return a path relative to the cwd, if it's not absolute + return fmt.Sprintf("%s/%s", in, path) } - // return a path relative to the cwd, if it's not absolute - return fmt.Sprintf("%s/%s", in, path) } + return path } @@ -198,9 +211,9 @@ func (pres *Presenter) locations(m match.Match) []*sarif.Location { var logicalLocations []*sarif.LogicalLocation - switch pres.srcMetadata.Scheme { - case source.ImageScheme: - img := pres.srcMetadata.ImageMetadata.UserInput + switch metadata := pres.src.Metadata.(type) { + case source.StereoscopeImageSourceMetadata: + img := metadata.UserInput locations := m.Package.Locations.ToSlice() for _, l := range locations { trimmedPath := strings.TrimPrefix(pres.locationPath(l), "/") @@ -215,15 +228,15 @@ func (pres *Presenter) locations(m match.Match) []*sarif.Location { // TODO we could add configuration to specify the prefix, a user might want to specify an image name and architecture // in the case of multiple vuln scans, for example physicalLocation = fmt.Sprintf("image/%s", physicalLocation) - case source.FileScheme: + case source.FileSourceMetadata: locations := m.Package.Locations.ToSlice() for _, l := range locations { logicalLocations = append(logicalLocations, &sarif.LogicalLocation{ - FullyQualifiedName: sp(fmt.Sprintf("%s:/%s", pres.srcMetadata.Path, pres.locationPath(l))), + FullyQualifiedName: sp(fmt.Sprintf("%s:/%s", metadata.Path, pres.locationPath(l))), Name: sp(l.RealPath), }) } - case source.DirectoryScheme: + case source.DirectorySourceMetadata: // DirectoryScheme is already handled, with input prepended if needed } @@ -399,7 +412,7 @@ func (pres *Presenter) resultMessage(m match.Match) sarif.Message { path := pres.packagePath(m.Package) message := fmt.Sprintf("The path %s reports %s at version %s ", path, m.Package.Name, m.Package.Version) - if pres.srcMetadata.Scheme == source.DirectoryScheme { + if _, ok := pres.src.Metadata.(source.DirectorySourceMetadata); ok { message = fmt.Sprintf("%s which would result in a vulnerable (%s) package installed", message, m.Package.Type) } else { message = fmt.Sprintf("%s which is a vulnerable (%s) package installed in the container", message, m.Package.Type) diff --git a/grype/presenter/sarif/presenter_test.go b/grype/presenter/sarif/presenter_test.go index a83fbc7c1e8..f770f5691a6 100644 --- a/grype/presenter/sarif/presenter_test.go +++ b/grype/presenter/sarif/presenter_test.go @@ -10,8 +10,10 @@ import ( "github.com/anchore/go-testutils" "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/presenter/internal" "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/grype/grype/vulnerability" + "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/source" ) @@ -20,15 +22,15 @@ var update = flag.Bool("update", false, "update .golden files for sarif presente func TestSarifPresenter(t *testing.T) { tests := []struct { name string - scheme source.Scheme + scheme internal.SyftSource }{ { name: "directory", - scheme: source.DirectoryScheme, + scheme: internal.DirectorySource, }, { name: "image", - scheme: source.ImageScheme, + scheme: internal.ImageSource, }, } @@ -36,7 +38,7 @@ func TestSarifPresenter(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { var buffer bytes.Buffer - matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, tc.scheme) + matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, tc.scheme) pb := models.PresenterConfig{ Matches: matches, @@ -57,8 +59,8 @@ func TestSarifPresenter(t *testing.T) { } var expected = testutils.GetGoldenFileContents(t) - actual = models.Redact(actual) - expected = models.Redact(expected) + actual = internal.Redact(actual) + expected = internal.Redact(expected) if !bytes.Equal(expected, actual) { assert.JSONEq(t, string(expected), string(actual)) @@ -70,83 +72,92 @@ func TestSarifPresenter(t *testing.T) { func Test_locationPath(t *testing.T) { tests := []struct { name string - path string - scheme source.Scheme + metadata any real string virtual string expected string }{ { - name: "dir:.", - scheme: source.DirectoryScheme, - path: ".", + name: "dir:.", + metadata: source.DirectorySourceMetadata{ + Path: ".", + }, real: "/home/usr/file", virtual: "file", expected: "file", }, { - name: "dir:./", - scheme: source.DirectoryScheme, - path: "./", + name: "dir:./", + metadata: source.DirectorySourceMetadata{ + Path: "./", + }, real: "/home/usr/file", virtual: "file", expected: "file", }, { - name: "dir:./someplace", - scheme: source.DirectoryScheme, - path: "./someplace", + name: "dir:./someplace", + metadata: source.DirectorySourceMetadata{ + Path: "./someplace", + }, real: "/home/usr/file", virtual: "file", expected: "someplace/file", }, { - name: "dir:/someplace", - scheme: source.DirectoryScheme, - path: "/someplace", + name: "dir:/someplace", + metadata: source.DirectorySourceMetadata{ + Path: "/someplace", + }, real: "file", expected: "/someplace/file", }, { - name: "dir:/someplace symlink", - scheme: source.DirectoryScheme, - path: "/someplace", + name: "dir:/someplace symlink", + metadata: source.DirectorySourceMetadata{ + Path: "/someplace", + }, real: "/someplace/usr/file", virtual: "file", expected: "/someplace/file", }, { - name: "dir:/someplace absolute", - scheme: source.DirectoryScheme, - path: "/someplace", + name: "dir:/someplace absolute", + metadata: source.DirectorySourceMetadata{ + Path: "/someplace", + }, real: "/usr/file", expected: "/usr/file", }, { - name: "file:/someplace/file", - scheme: source.FileScheme, - path: "/someplace/file", + name: "file:/someplace/file", + metadata: source.FileSourceMetadata{ + Path: "/someplace/file", + }, real: "/usr/file", expected: "/usr/file", }, { - name: "file:/someplace/file relative", - scheme: source.FileScheme, - path: "/someplace/file", + name: "file:/someplace/file relative", + metadata: source.FileSourceMetadata{ + Path: "/someplace/file", + }, real: "file", expected: "file", }, { - name: "image", - scheme: source.ImageScheme, - path: "alpine:latest", + name: "image", + metadata: source.StereoscopeImageSourceMetadata{ + UserInput: "alpine:latest", + }, real: "/etc/file", expected: "/etc/file", }, { - name: "image symlink", - scheme: source.ImageScheme, - path: "alpine:latest", + name: "image symlink", + metadata: source.StereoscopeImageSourceMetadata{ + UserInput: "alpine:latest", + }, real: "/etc/elsewhere/file", virtual: "/etc/file", expected: "/etc/file", @@ -155,15 +166,14 @@ func Test_locationPath(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - pres := createDirPresenter(t, test.path) - pres.srcMetadata = &source.Metadata{ - Scheme: test.scheme, - Path: test.path, + pres := createDirPresenter(t) + pres.src = &source.Description{ + Metadata: test.metadata, } path := pres.packagePath(pkg.Package{ - Locations: source.NewLocationSet( - source.NewVirtualLocation(test.real, test.virtual), + Locations: file.NewLocationSet( + file.NewVirtualLocation(test.real, test.virtual), ), }) @@ -172,19 +182,21 @@ func Test_locationPath(t *testing.T) { } } -func createDirPresenter(t *testing.T, path string) *Presenter { - matches, packages, _, metadataProvider, _, _ := models.GenerateAnalysis(t, source.DirectoryScheme) - s, err := source.NewFromDirectory(path) +func createDirPresenter(t *testing.T) *Presenter { + matches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.DirectorySource) + d := t.TempDir() + s, err := source.NewFromDirectory(source.DirectoryConfig{Path: d}) if err != nil { t.Fatal(err) } + desc := s.Describe() pb := models.PresenterConfig{ Matches: matches, Packages: packages, MetadataProvider: metadataProvider, Context: pkg.Context{ - Source: &s.Metadata, + Source: &desc, }, } @@ -196,12 +208,12 @@ func createDirPresenter(t *testing.T, path string) *Presenter { func TestToSarifReport(t *testing.T) { tt := []struct { name string - scheme source.Scheme + scheme internal.SyftSource locations map[string]string }{ { name: "directory", - scheme: source.DirectoryScheme, + scheme: internal.DirectorySource, locations: map[string]string{ "CVE-1999-0001-package-1": "/some/path/somefile-1.txt", "CVE-1999-0002-package-2": "/some/path/somefile-2.txt", @@ -209,7 +221,7 @@ func TestToSarifReport(t *testing.T) { }, { name: "image", - scheme: source.ImageScheme, + scheme: internal.ImageSource, locations: map[string]string{ "CVE-1999-0001-package-1": "image/somefile-1.txt", "CVE-1999-0002-package-2": "image/somefile-2.txt", @@ -222,7 +234,7 @@ func TestToSarifReport(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - matches, packages, context, metadataProvider, _, _ := models.GenerateAnalysis(t, tc.scheme) + matches, packages, context, metadataProvider, _, _ := internal.GenerateAnalysis(t, tc.scheme) pb := models.PresenterConfig{ Matches: matches, diff --git a/grype/presenter/table/presenter_test.go b/grype/presenter/table/presenter_test.go index fea1d41468e..61b04d5e8df 100644 --- a/grype/presenter/table/presenter_test.go +++ b/grype/presenter/table/presenter_test.go @@ -12,10 +12,10 @@ import ( "github.com/anchore/go-testutils" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/presenter/internal" "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/grype/grype/vulnerability" syftPkg "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/source" ) var update = flag.Bool("update", false, "update the *.golden files for table presenters") @@ -76,7 +76,7 @@ func TestCreateRow(t *testing.T) { func TestTablePresenter_Color(t *testing.T) { var buffer bytes.Buffer - matches, packages, _, metadataProvider, _, _ := models.GenerateAnalysis(t, source.ImageScheme) + matches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysis(t, internal.ImageSource) pb := models.PresenterConfig{ Matches: matches, @@ -209,7 +209,7 @@ func TestRemoveDuplicateRows(t *testing.T) { func TestHidesIgnoredMatches(t *testing.T) { var buffer bytes.Buffer - matches, ignoredMatches, packages, _, metadataProvider, _, _ := models.GenerateAnalysisWithIgnoredMatches(t, source.ImageScheme) + matches, ignoredMatches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysisWithIgnoredMatches(t, internal.ImageSource) pb := models.PresenterConfig{ Matches: matches, @@ -240,7 +240,7 @@ func TestHidesIgnoredMatches(t *testing.T) { func TestDisplaysIgnoredMatches(t *testing.T) { var buffer bytes.Buffer - matches, ignoredMatches, packages, _, metadataProvider, _, _ := models.GenerateAnalysisWithIgnoredMatches(t, source.ImageScheme) + matches, ignoredMatches, packages, _, metadataProvider, _, _ := internal.GenerateAnalysisWithIgnoredMatches(t, internal.ImageSource) pb := models.PresenterConfig{ Matches: matches, diff --git a/grype/presenter/template/presenter_test.go b/grype/presenter/template/presenter_test.go index 9acaf6cd59e..502f8ca3f75 100644 --- a/grype/presenter/template/presenter_test.go +++ b/grype/presenter/template/presenter_test.go @@ -11,14 +11,14 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/go-testutils" + "github.com/anchore/grype/grype/presenter/internal" "github.com/anchore/grype/grype/presenter/models" - "github.com/anchore/syft/syft/source" ) var update = flag.Bool("update", false, "update the *.golden files for template presenters") func TestPresenter_Present(t *testing.T) { - matches, packages, context, metadataProvider, appConfig, dbStatus := models.GenerateAnalysis(t, source.ImageScheme) + matches, packages, context, metadataProvider, appConfig, dbStatus := internal.GenerateAnalysis(t, internal.ImageSource) workingDirectory, err := os.Getwd() if err != nil { @@ -53,7 +53,7 @@ func TestPresenter_Present(t *testing.T) { } func TestPresenter_SprigDate_Fails(t *testing.T) { - matches, packages, context, metadataProvider, appConfig, dbStatus := models.GenerateAnalysis(t, source.ImageScheme) + matches, packages, context, metadataProvider, appConfig, dbStatus := internal.GenerateAnalysis(t, internal.ImageSource) workingDirectory, err := os.Getwd() require.NoError(t, err) diff --git a/templates/csv.tmpl b/templates/csv.tmpl new file mode 100644 index 00000000000..738185ffc42 --- /dev/null +++ b/templates/csv.tmpl @@ -0,0 +1,4 @@ +"Package","Version Installed","Vulnerability ID","Severity" +{{- range .Matches}} +"{{.Artifact.Name}}","{{.Artifact.Version}}","{{.Vulnerability.ID}}","{{.Vulnerability.Severity}}" +{{- end}} diff --git a/test/integration/match_by_image_test.go b/test/integration/match_by_image_test.go index 75f84cd84ac..1516e60f723 100644 --- a/test/integration/match_by_image_test.go +++ b/test/integration/match_by_image_test.go @@ -606,13 +606,15 @@ func TestMatchByImage(t *testing.T) { userImage := "docker-archive:" + tarPath - sourceInput, err := source.ParseInput(userImage, "") + detection, err := source.Detect(userImage, source.DetectConfig{}) require.NoError(t, err) // this is purely done to help setup mocks - theSource, cleanup, err := source.New(*sourceInput, nil, nil) + theSource, err := detection.NewSource(source.DetectionSourceConfig{}) require.NoError(t, err) - defer cleanup() + t.Cleanup(func() { + require.NoError(t, theSource.Close()) + }) // TODO: relationships are not verified at this time config := cataloger.DefaultConfig() @@ -645,7 +647,7 @@ func TestMatchByImage(t *testing.T) { } // build expected matches from what's discovered from the catalog - expectedMatches := test.expectedFn(*theSource, collection, theStore) + expectedMatches := test.expectedFn(theSource, collection, theStore) assertMatches(t, expectedMatches.Sorted(), actualResults.Sorted()) }) diff --git a/test/integration/utils_test.go b/test/integration/utils_test.go index b76686e4a9d..c86ae5c6264 100644 --- a/test/integration/utils_test.go +++ b/test/integration/utils_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/require" "github.com/anchore/grype/grype/match" "github.com/anchore/syft/syft" @@ -70,16 +71,18 @@ func saveImage(t testing.TB, imageName string, destPath string) { } func getSyftSBOM(t testing.TB, image string, format sbom.Format) string { - sourceInput, err := source.ParseInput(image, "") + detection, err := source.Detect(image, source.DetectConfig{}) if err != nil { t.Fatalf("could not generate source input for packages command: %+v", err) } - src, cleanup, err := source.New(*sourceInput, nil, nil) + src, err := detection.NewSource(source.DetectionSourceConfig{}) if err != nil { t.Fatalf("can't get the source: %+v", err) } - t.Cleanup(cleanup) + t.Cleanup(func() { + require.NoError(t, src.Close()) + }) config := cataloger.DefaultConfig() config.Search.Scope = source.SquashedScope @@ -91,7 +94,7 @@ func getSyftSBOM(t testing.TB, image string, format sbom.Format) string { Packages: collection, LinuxDistribution: distro, }, - Source: src.Metadata, + Source: src.Describe(), } bytes, err := syft.Encode(s, format) diff --git a/test/quality/.yardstick.yaml b/test/quality/.yardstick.yaml index 1ed3389042d..e580fec4b9d 100644 --- a/test/quality/.yardstick.yaml +++ b/test/quality/.yardstick.yaml @@ -32,7 +32,6 @@ x-ref: - docker.io/anchore/test_images:alpine-package-cpe-vuln-match-bd0aaef@sha256:0825acea611c7c5cc792bc7cc20de44d7413fd287dc5afc4aab9c1891d037b4f - docker.io/alpine:3.2@sha256:ddac200f3ebc9902fb8cfcd599f41feb2151f1118929da21bcef57dc276975f9 - docker.io/centos:6@sha256:3688aa867eb84332460e172b9250c9c198fdfd8d987605fd53f246f498c60bcf - - docker.io/ubuntu:16.10@sha256:8dc9652808dc091400d7d5983949043a9f9c7132b15c14814275d25f94bca18a - docker.io/almalinux:8@sha256:cd49d7250ed7bb194d502d8a3e50bd775055ca275d1d9c2785aea72b890afe6a - docker.io/rockylinux:8@sha256:72afc2e1a20c9ddf56a81c51148ebcbe927c0a879849efe813bee77d69df1dd8 - docker.io/oraclelinux:6@sha256:a06327c0f1d18d753f2a60bb17864c84a850bb6dcbcf5946dd1a8123f6e75495 @@ -42,7 +41,7 @@ x-ref: - registry.access.redhat.com/ubi8@sha256:68fecea0d255ee253acbf0c860eaebb7017ef5ef007c25bee9eeffd29ce85b29 - docker.io/python:3.8.0-slim@sha256:5e96e03a493a54904aa8be573fc0414431afb4f47ac58fbffd03b2a725005364 - docker.io/ghost:5.2.4@sha256:42137b9bd1faf4cdea5933279c48a912d010ef614551aeb0e44308600aa3e69f - - docker.io/node:14.1.0-slim@sha256:d8a88e8e15fd26eee7734c9f60531b5ad2abdc3d663be0d818ed26159db80512 + - docker.io/node:4.2.1-slim@sha256:af31633b87d0dc58c306b04ad9f6ca88104626363c5c085e9962832628eb09ce - docker.io/elastic/kibana:8.5.0@sha256:b9e3e52f61e0a347e38eabe80ba0859f859023bc0cc8836410320aa7eb5d3e02 - docker.io/jenkins/jenkins:2.361.4-lts-jdk11@sha256:6fd5699ab182b5d23d0e3936de6047edc30955a3a92e01c392d5a2fd583efac0 - docker.io/neo4j:4.4.14-community@sha256:fcfcbb026e0e538bf66f5fe5c4b2db3dd4931c3aae07f13a5a8c10e979596256 @@ -52,6 +51,33 @@ x-ref: - docker.io/postgres:13.2@sha256:1a67ab960138c479d66834cd6bcb5b5582c53869e6052dbf4ff48d4a94c13da3 - ghcr.io/chainguard-images/scanner-test@sha256:59bddc101fba0c45d5c093575c6bc5bfee7f0e46ff127e6bb4e5acaaafb525f9 - docker.io/keycloak/keycloak:21.0.2@sha256:347a0d748d05a050dc64b92de2246d2240db6eb38afbc17c3c08d0acb0db1b50 + - docker.io/datawire/aes:3.6.0@sha256:86a072278135462b6cbef70e89894df8f9b20f428b361fda2132fbb442ef257b + - docker.io/bitnami/spark:3.2.4-debian-11-r8@sha256:267d5a6345636710b4b57b7fe981c9760203e7e092c705416310ea30a9806d74 + - mcr.microsoft.com/cbl-mariner/base/core:2.0.20220731-arm64@sha256:51101e635f56032d5afd3fb56d66c7b93b34d5a39ddac01695d62b94473cc34e + - docker.io/grafana/grafana:9.2.4@sha256:a11c6829cdfe7fd791e48ba5b511f3562384361fb4c568ec2d8a5041ac52babe + - docker.io/hashicorp/vault:1.12.0@sha256:09354ca0891f7cee8fbfe8db08c62d2d757fad8ae6c91f2b6cce7a34440e3fae + - docker.io/ubuntu:12.04@sha256:18305429afa14ea462f810146ba44d4363ae76e4c8dfc38288cf73aa07485005 + - docker.io/ubuntu:12.10@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7 + - docker.io/ubuntu:13.04@sha256:bc48dd7075ce920ebbaa4581d3200e9fb3aaec31591061d7e3a280a04ef0248c + - docker.io/ubuntu:14.04@sha256:881afbae521c910f764f7187dbfbca3cc10c26f8bafa458c76dda009a901c29d + - docker.io/ubuntu:14.10@sha256:6341c688b4b0b82ec735389b3c97df8cf2831b8cb8bd1856779130a86574ac5c + - docker.io/ubuntu:15.04@sha256:2fb27e433b3ecccea2a14e794875b086711f5d49953ef173d8a03e8707f1510f + - docker.io/ubuntu:15.10@sha256:02521a2d079595241c6793b2044f02eecf294034f31d6e235ac4b2b54ffc41f3 + - docker.io/ubuntu:16.10@sha256:8dc9652808dc091400d7d5983949043a9f9c7132b15c14814275d25f94bca18a + - docker.io/ubuntu:17.04@sha256:213e05583a7cb8756a3f998e6dd65204ddb6b4c128e2175dcdf174cdf1877459 + - docker.io/ubuntu:17.10@sha256:9c4bf7dbb981591d4a1169138471afe4bf5ff5418841d00e30a7ba372e38d6c1 + - docker.io/ubuntu:18.04@sha256:971a12d7e92a23183dead8bfc415aa650e7deb1cc5fed11a3d21f759a891fde9 + - docker.io/ubuntu:18.10@sha256:c95b7b93ccd48c3bfd97f8cac6d5ca8053ced584c9e8e6431861ca30b0d73114 + - docker.io/ubuntu:19.04@sha256:3db17bfc30b41cc18552578f4a66d7010050eb9fdc42bf6c3d82bb0dcdf88d58 + - docker.io/ubuntu:19.10@sha256:6852f9e05c5bce8aa77173fa83ce611f69f271ee3a16503c5f80c199969fd1eb + - docker.io/ubuntu:20.04@sha256:9d42d0e3e57bc067d10a75ee33bdd1a5298e95e5fc3c5d1fce98b455cb879249 + - docker.io/ubuntu:20.10@sha256:754eb641a1ba98a8b483c3595a14164fa4ed7f4b457e1aa05f13ce06f8151723 + - docker.io/ubuntu:21.04@sha256:cb92f03e258f965442b883f5402b310dd7a5ea0a661a865ad02a42bc21234bf7 + - docker.io/ubuntu:21.10@sha256:253908b2844746ab3f3a08fc8a44b9b9fc1efc408d5969b093ab9ffa11eb1894 + - docker.io/ubuntu:22.04@sha256:aa6c2c047467afc828e77e306041b7fa4a65734fe3449a54aa9c280822b0d87d + - docker.io/ubuntu:22.10@sha256:80fb4ea0c0a384a3072a6be1879c342bb636b0d105209535ba893ba75ab38ede + - docker.io/ubuntu:23.04@sha256:09f035f46361d193ded647342903b413d57d05cc06acff8285f9dda9f2d269d5 + - gcr.io/distroless/python3-debian11@sha256:69ae7f133d33faab720af28e78fb45707b623bcbc94ae02a07c633bf053f4b40 # new vulnerabilities are added all of the time, instead of keeping up it's easier to ignore newer entries. # This approach helps tremendously with keeping the analysis relatively stable. diff --git a/test/quality/vulnerability-match-labels b/test/quality/vulnerability-match-labels index 5f5bfa96fbe..e68f57ccf6f 160000 --- a/test/quality/vulnerability-match-labels +++ b/test/quality/vulnerability-match-labels @@ -1 +1 @@ -Subproject commit 5f5bfa96fbe993649fa2c5eba81c788d1cc263e2 +Subproject commit e68f57ccf6f2c627014cdca5e9777539525505ab