diff --git a/.github/workflows/bypass-test.yaml b/.github/workflows/bypass-test.yaml index 3a3102e3e574..93c5ba2869d3 100644 --- a/.github/workflows/bypass-test.yaml +++ b/.github/workflows/bypass-test.yaml @@ -9,6 +9,7 @@ on: - 'mkdocs.yml' - 'LICENSE' - '.release-please-manifest.json' + - 'helm/trivy/Chart.yaml' pull_request: paths: - '**.md' @@ -16,6 +17,7 @@ on: - 'mkdocs.yml' - 'LICENSE' - '.release-please-manifest.json' + - 'helm/trivy/Chart.yaml' jobs: test: name: Test diff --git a/.github/workflows/cache-test-images.yaml b/.github/workflows/cache-test-images.yaml index a03dc683e3ae..4b73cc2fb5bc 100644 --- a/.github/workflows/cache-test-images.yaml +++ b/.github/workflows/cache-test-images.yaml @@ -27,7 +27,8 @@ jobs: if: github.ref_name == 'main' id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT @@ -67,7 +68,8 @@ jobs: if: github.ref_name == 'main' id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-vm-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_VM_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT diff --git a/.github/workflows/publish-chart.yaml b/.github/workflows/publish-chart.yaml index 3a7db4970065..15c8d84da31d 100644 --- a/.github/workflows/publish-chart.yaml +++ b/.github/workflows/publish-chart.yaml @@ -4,6 +4,11 @@ name: Publish Helm chart on: workflow_dispatch: pull_request: + types: + - opened + - synchronize + - reopened + - closed branches: - main paths: @@ -18,8 +23,10 @@ env: KIND_VERSION: "v0.14.0" KIND_IMAGE: "kindest/node:v1.23.6@sha256:b1fa224cc6c7ff32455e0b1fd9cbfd3d3bc87ecaa8fcb06961ed1afb3db0f9ae" jobs: + # `test-chart` job starts if a PR with Helm Chart is created, merged etc. test-chart: - runs-on: ubuntu-20.04 + if: github.event_name != 'push' + runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4.1.6 @@ -28,11 +35,12 @@ jobs: - name: Install Helm uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 with: - version: v3.5.0 + version: v3.14.4 - name: Set up python uses: actions/setup-python@v5 with: - python-version: 3.7 + python-version: '3.x' + check-latest: true - name: Setup Chart Linting id: lint uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 @@ -48,11 +56,39 @@ jobs: sed -i -e '136s,false,'true',g' ./helm/trivy/values.yaml ct lint-and-install --validate-maintainers=false --charts helm/trivy + # `update-chart-version` job starts if a new tag is pushed + update-chart-version: + if: github.event_name == 'push' + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4.1.6 + with: + fetch-depth: 0 + - name: Set up Git user + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + + - name: Install tools + uses: aquaproj/aqua-installer@v3.0.1 + with: + aqua_version: v1.25.0 + aqua_opts: "" + + - name: Create a PR with Trivy version + run: mage helm:updateVersion + env: + # Use ORG_REPO_TOKEN instead of GITHUB_TOKEN + # This allows the created PR to trigger tests and other workflows + GITHUB_TOKEN: ${{ secrets.ORG_REPO_TOKEN }} + + # `publish-chart` job starts if a PR with a new Helm Chart is merged or manually publish-chart: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' needs: - test-chart - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4.1.6 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 74b0933c6ef6..05edadf93331 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -7,6 +7,7 @@ on: - 'mkdocs.yml' - 'LICENSE' - '.release-please-manifest.json' ## don't run tests for release-please PRs + - 'helm/trivy/Chart.yaml' merge_group: workflow_dispatch: @@ -90,7 +91,8 @@ jobs: - name: Generate image list digest id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT @@ -147,7 +149,8 @@ jobs: - name: Generate image list digest id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT @@ -185,7 +188,8 @@ jobs: - name: Generate image list digest id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-vm-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_VM_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT diff --git a/.golangci.yaml b/.golangci.yaml index 95c66cc69f73..40bfa36e1a64 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -105,6 +105,7 @@ linters: - typecheck - unconvert - unused + - usestdlibvars run: go: '1.22' diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 208746a81be3..0fd1f968a6d2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1 @@ -{".":"0.56.0"} +{".":"0.57.0"} diff --git a/CHANGELOG.md b/CHANGELOG.md index afc44ba7a956..f4bad92b11d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,51 @@ # Changelog +## [0.57.0](https://github.com/aquasecurity/trivy/compare/v0.56.0...v0.57.0) (2024-10-31) + + +### ⚠ BREAKING CHANGES + +* **k8s:** support k8s multi container ([#7444](https://github.com/aquasecurity/trivy/issues/7444)) + +### Features + +* add end of life date for Ubuntu 24.10 ([#7787](https://github.com/aquasecurity/trivy/issues/7787)) ([ad3c09e](https://github.com/aquasecurity/trivy/commit/ad3c09e006e134f3c5b879ffc34ce9895a8c860f)) +* **cli:** add `trivy auth` ([#7664](https://github.com/aquasecurity/trivy/issues/7664)) ([27117f8](https://github.com/aquasecurity/trivy/commit/27117f81d52483c3ceec56fe56ac298e242fbc9a)) +* **cli:** error out when ignore file cannot be found ([#7624](https://github.com/aquasecurity/trivy/issues/7624)) ([cb0b3a9](https://github.com/aquasecurity/trivy/commit/cb0b3a9279b31810ecd686a385e5140e567ce86f)) +* **cli:** rename `trivy auth` to `trivy registry` ([#7727](https://github.com/aquasecurity/trivy/issues/7727)) ([633a7ab](https://github.com/aquasecurity/trivy/commit/633a7abeea4287899392a24f2705f96dfeb7e312)) +* **cyclonedx:** add file checksums to `CycloneDX` reports ([#7507](https://github.com/aquasecurity/trivy/issues/7507)) ([c225883](https://github.com/aquasecurity/trivy/commit/c225883649f58128a99fa2c1cef327d0e57940be)) +* **db:** append errors ([#7843](https://github.com/aquasecurity/trivy/issues/7843)) ([5e78b6c](https://github.com/aquasecurity/trivy/commit/5e78b6c12fb5740c12dedeea3d335d48ec2f752b)) +* **misconf:** export unresolvable field of IaC types to Rego ([#7765](https://github.com/aquasecurity/trivy/issues/7765)) ([9514148](https://github.com/aquasecurity/trivy/commit/9514148767865baddd73a49245385574927f7a74)) +* **misconf:** public network support for Azure Storage Account ([#7601](https://github.com/aquasecurity/trivy/issues/7601)) ([ad91412](https://github.com/aquasecurity/trivy/commit/ad914123c4d203af1e1da6b7e2d3e49d9d3831d8)) +* **misconf:** Show misconfig ID in output ([#7762](https://github.com/aquasecurity/trivy/issues/7762)) ([f75c0d1](https://github.com/aquasecurity/trivy/commit/f75c0d1f0069d4856cb4826d6049f32c5b9409d9)) +* **misconf:** ssl_mode support for GCP SQL DB instance ([#7564](https://github.com/aquasecurity/trivy/issues/7564)) ([2eaa17e](https://github.com/aquasecurity/trivy/commit/2eaa17e0717940b27a79050e2efd9213b71178c9)) +* **parser:** ignore white space in pom.xml files ([#7747](https://github.com/aquasecurity/trivy/issues/7747)) ([a7baa93](https://github.com/aquasecurity/trivy/commit/a7baa93b00b8636aa097e64cdb8eed97dbd68511)) +* **report:** update gitlab template to populate operating_system value ([#7735](https://github.com/aquasecurity/trivy/issues/7735)) ([c0d79fa](https://github.com/aquasecurity/trivy/commit/c0d79fa09e645f3a3dbff878e393b8631fb17b64)) + + +### Bug Fixes + +* **cli:** `clean --all` deletes only relevant dirs ([#7704](https://github.com/aquasecurity/trivy/issues/7704)) ([672e886](https://github.com/aquasecurity/trivy/commit/672e886aed152ae0f09a16941706746f3053ca94)) +* **cli:** add config name to skip-policy-update alias ([#7820](https://github.com/aquasecurity/trivy/issues/7820)) ([b661d68](https://github.com/aquasecurity/trivy/commit/b661d680ff0372c8e4beea0db13bf69d6a2203a8)) +* **db:** fix javadb downloading error handling ([#7642](https://github.com/aquasecurity/trivy/issues/7642)) ([2c87f0c](https://github.com/aquasecurity/trivy/commit/2c87f0cb794acd77446a273582ba1a45b9f18980)) +* enable usestdlibvars linter ([#7770](https://github.com/aquasecurity/trivy/issues/7770)) ([57e24aa](https://github.com/aquasecurity/trivy/commit/57e24aa85382f749df7f673e241caaf3fcbb45cb)) +* **go:** Do not trim v prefix from versions in Go Mod Analyzer ([#7733](https://github.com/aquasecurity/trivy/issues/7733)) ([e872ec0](https://github.com/aquasecurity/trivy/commit/e872ec006c0745a5a142728af0096c6d6bb9ddf3)) +* **helm:** properly handle multiple archived dependencies ([#7782](https://github.com/aquasecurity/trivy/issues/7782)) ([6fab88d](https://github.com/aquasecurity/trivy/commit/6fab88dd56c257ef2cc63b617c2a5decb1c4cf98)) +* **java:** correctly inherit `version` and `scope` from upper/root `depManagement` and `dependencies` into parents ([#7541](https://github.com/aquasecurity/trivy/issues/7541)) ([778df82](https://github.com/aquasecurity/trivy/commit/778df828eaad9827cb833c6285058a33aa2b83ca)) +* **k8s:** skip resources without misconfigs ([#7797](https://github.com/aquasecurity/trivy/issues/7797)) ([7882776](https://github.com/aquasecurity/trivy/commit/78827768a612ab305bf9c55409ce76d6774302a5)) +* **k8s:** support k8s multi container ([#7444](https://github.com/aquasecurity/trivy/issues/7444)) ([c434775](https://github.com/aquasecurity/trivy/commit/c4347759234dcb5f372b07f92fb4230ef391d710)) +* **k8s:** support kubernetes v1.31 ([#7810](https://github.com/aquasecurity/trivy/issues/7810)) ([7a4f4d8](https://github.com/aquasecurity/trivy/commit/7a4f4d8b12996687f3095a2042cdf2f5985332c9)) +* **license:** fix license normalization for Universal Permissive License ([#7766](https://github.com/aquasecurity/trivy/issues/7766)) ([f6acdf7](https://github.com/aquasecurity/trivy/commit/f6acdf713991f8ffdbe765178fcb8a9cde433cba)) +* **misconf:** change default ACL of digitalocean_spaces_bucket to private ([#7577](https://github.com/aquasecurity/trivy/issues/7577)) ([9da84f5](https://github.com/aquasecurity/trivy/commit/9da84f54fadbe6ad0d73983952e945ed63b666f3)) +* **misconf:** check if property is not nil before conversion ([#7578](https://github.com/aquasecurity/trivy/issues/7578)) ([c8c14d3](https://github.com/aquasecurity/trivy/commit/c8c14d36245623019f29d258f813d2325f7490f7)) +* **misconf:** fix for Azure Storage Account network acls adaptation ([#7602](https://github.com/aquasecurity/trivy/issues/7602)) ([35fd018](https://github.com/aquasecurity/trivy/commit/35fd018ae7ad86823f114f0ac2f1376726aee444)) +* **misconf:** properly expand dynamic blocks ([#7612](https://github.com/aquasecurity/trivy/issues/7612)) ([8d5dbc9](https://github.com/aquasecurity/trivy/commit/8d5dbc9fec3569b22ed81a03c40eaf732768718b)) +* **redhat:** include arch in PURL qualifiers ([#7654](https://github.com/aquasecurity/trivy/issues/7654)) ([a585e95](https://github.com/aquasecurity/trivy/commit/a585e95f3398631d9ad10505c5ff642fde21aef7)) +* **repo:** `git clone` output to Stderr ([#7561](https://github.com/aquasecurity/trivy/issues/7561)) ([fdf203c](https://github.com/aquasecurity/trivy/commit/fdf203cd209aeb40f454bd12d121a54d6ed7a542)) +* **report:** Fix invalid URI in SARIF report ([#7645](https://github.com/aquasecurity/trivy/issues/7645)) ([015bb88](https://github.com/aquasecurity/trivy/commit/015bb885ac414b91201fa9791eead395d878149c)) +* **sbom:** add options for DBs in private registries ([#7660](https://github.com/aquasecurity/trivy/issues/7660)) ([1f2e91b](https://github.com/aquasecurity/trivy/commit/1f2e91b02b3606dd11963002a8cfac7962f3478f)) +* **sbom:** use `Annotation` instead of `AttributionTexts` for `SPDX` formats ([#7811](https://github.com/aquasecurity/trivy/issues/7811)) ([f2bb9c6](https://github.com/aquasecurity/trivy/commit/f2bb9c6227743dd61f44eb591d4b15192fe110c6)) + ## [0.56.0](https://github.com/aquasecurity/trivy/compare/v0.55.0...v0.56.0) (2024-10-03) diff --git a/contrib/Trivy.gitlab-ci.yml b/contrib/Trivy.gitlab-ci.yml index 66225b0dac06..f91588240346 100644 --- a/contrib/Trivy.gitlab-ci.yml +++ b/contrib/Trivy.gitlab-ci.yml @@ -12,9 +12,9 @@ Trivy_container_scanning: before_script: - export TRIVY_VERSION=${TRIVY_VERSION:-v0.19.2} - apk add --no-cache curl docker-cli - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin ${TRIVY_VERSION} - curl -sSL -o /tmp/trivy-gitlab.tpl https://github.com/aquasecurity/trivy/raw/${TRIVY_VERSION}/contrib/gitlab.tpl + - trivy registry login --username "$CI_REGISTRY_USER" --password "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/tmp/trivy-gitlab.tpl" -o gl-container-scanning-report.json $IMAGE cache: diff --git a/contrib/gitlab.tpl b/contrib/gitlab.tpl index 744c0c9394cb..7ff8abd8bfec 100644 --- a/contrib/gitlab.tpl +++ b/contrib/gitlab.tpl @@ -24,11 +24,18 @@ "status": "success", "type": "container_scanning" }, + {{- $image := "Unknown" -}} + {{- $os := "Unknown" -}} + {{- range . }} + {{- if eq .Class "os-pkgs" -}} + {{- $target := .Target }} + {{- $image = $target | regexFind "[^\\s]+" }} + {{- $os = $target | splitList "(" | last | trimSuffix ")" }} + {{- end }} + {{- end }} "vulnerabilities": [ {{- $t_first := true }} {{- range . }} - {{- $target := .Target }} - {{- $image := $target | regexFind "[^\\s]+" }} {{- range .Vulnerabilities -}} {{- if $t_first -}} {{- $t_first = false -}} @@ -65,7 +72,7 @@ "version": "{{ .InstalledVersion }}" }, {{- /* TODO: No mapping available - https://github.com/aquasecurity/trivy/issues/332 */}} - "operating_system": "Unknown", + "operating_system": "{{ $os }}", "image": "{{ $image }}" }, "identifiers": [ diff --git a/contrib/junit.tpl b/contrib/junit.tpl index b01bb9fd22dd..27b654b7b049 100644 --- a/contrib/junit.tpl +++ b/contrib/junit.tpl @@ -16,7 +16,7 @@ {{- if .MisconfSummary }} - + {{- else }} {{- end }} diff --git a/docs/docs/advanced/private-registries/index.md b/docs/docs/advanced/private-registries/index.md index 7d5a594cf5b6..e40f2f8f7230 100644 --- a/docs/docs/advanced/private-registries/index.md +++ b/docs/docs/advanced/private-registries/index.md @@ -1,13 +1,30 @@ Trivy can download images from a private registry without the need for installing Docker or any other 3rd party tools. This makes it easy to run within a CI process. -## Credential -To use Trivy with private images, simply install it and provide your credentials: +## Login +You can log in to a private registry using the `trivy registry login` command. +It uses the Docker configuration file (`~/.docker/config.json`) to store the credentials under the hood, and the configuration file path can be configured by `DOCKER_CONFIG` environment variable. + +```shell +$ cat ~/my_password.txt | trivy registry login --username foo --password-stdin ghcr.io +$ trivy image ghcr.io/your/private_image +``` + +## Passing Credentials +You can also provide your credentials when scanning. ```shell $ TRIVY_USERNAME=YOUR_USERNAME TRIVY_PASSWORD=YOUR_PASSWORD trivy image YOUR_PRIVATE_IMAGE ``` +!!! warning + When passing credentials via environment variables or CLI flags, Trivy will attempt to use these credentials for all registries encountered during scanning, regardless of the target registry. + This can potentially lead to unintended credential exposure. + To mitigate this risk: + + 1. Set credentials cautiously and only when necessary. + 2. Prefer using `trivy registry login` to pre-configure credentials with specific registries, which ensures credentials are only sent to appropriate registries. + Trivy also supports providing credentials through CLI flags: ```shell @@ -17,6 +34,7 @@ $ TRIVY_PASSWORD=YOUR_PASSWORD trivy image --username YOUR_USERNAME YOUR_PRIVATE !!! warning The CLI flag `--password` is available, but its use is not recommended for security reasons. + You can also store your credentials in `trivy.yaml`. For more information, please refer to [the documentation](../../references/configuration/config-file.md). @@ -35,15 +53,5 @@ In the example above, Trivy attempts to use two pairs of credentials: Please note that the number of usernames and passwords must be the same. -## docker login -If you have Docker configured locally and have set up the credentials, Trivy can access them. - -```shell -$ docker login ghcr.io -Username: -Password: -$ trivy image ghcr.io/your/private_image -``` - !!! note - `docker login` can be used with any container runtime, such as Podman. + `--password-stdin` doesn't support comma-separated passwords. \ No newline at end of file diff --git a/docs/docs/configuration/filtering.md b/docs/docs/configuration/filtering.md index befcc54180ac..095e5c3bb559 100644 --- a/docs/docs/configuration/filtering.md +++ b/docs/docs/configuration/filtering.md @@ -112,7 +112,7 @@ trivy config --severity HIGH,CRITICAL examples/misconf/mixed Dockerfile (dockerfile) ======================= -Tests: 17 (SUCCESSES: 16, FAILURES: 1, EXCEPTIONS: 0) +Tests: 17 (SUCCESSES: 16, FAILURES: 1) Failures: 1 (HIGH: 1, CRITICAL: 0) HIGH: Last USER command in Dockerfile should not be 'root' @@ -130,13 +130,13 @@ See https://avd.aquasec.com/misconfig/ds002 deployment.yaml (kubernetes) ============================ -Tests: 8 (SUCCESSES: 8, FAILURES: 0, EXCEPTIONS: 0) +Tests: 8 (SUCCESSES: 8, FAILURES: 0) Failures: 0 (HIGH: 0, CRITICAL: 0) main.tf (terraform) =================== -Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) +Tests: 1 (SUCCESSES: 0, FAILURES: 1) Failures: 1 (HIGH: 0, CRITICAL: 1) CRITICAL: Classic resources should not be used. @@ -477,13 +477,13 @@ ignore { ``` ```bash -trivy image --ignore-policy contrib/example_policy/basic.rego centos:7 +trivy image --ignore-policy examples/ignore-policies/basic.rego centos:7 ``` For more advanced use cases, there is a built-in Rego library with helper functions that you can import into your policy using: `import data.lib.trivy`. More info about the helper functions are in the library [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/pkg/result/module.go). -You can find more example checks [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/pkg/result/module.go) +You can create a whitelist of checks using Rego, see the detailed [example](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/examples/ignore-policies/whitelist.rego). Additional examples are available [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/examples/ignore-policies). ### By Vulnerability Exploitability Exchange (VEX) | Scanner | Supported | diff --git a/docs/docs/configuration/reporting.md b/docs/docs/configuration/reporting.md index 17bf0b864283..39ecb6e5333c 100644 --- a/docs/docs/configuration/reporting.md +++ b/docs/docs/configuration/reporting.md @@ -5,7 +5,7 @@ Trivy supports the following formats: - Table - JSON -- [SARIF](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning) +- [SARIF][sarif-home] - Template - SBOM - GitHub dependency snapshot @@ -252,16 +252,19 @@ $ trivy image -f json -o results.json golang:1.12-alpine | Secret | ✓ | | License | ✓ | -[SARIF][sarif] can be generated with the `--format sarif` flag. +[SARIF][sarif-home] (Static Analysis Results Interchange Format) complying with [SARIF 2.1.0 OASIS standard][sarif-spec] can be generated with the `--format sarif` flag. ``` $ trivy image --format sarif -o report.sarif golang:1.12-alpine ``` -This SARIF file can be uploaded to GitHub code scanning results, and there is a [Trivy GitHub Action][action] for automating this process. +This SARIF file can be uploaded to several platforms, including: + +- [GitHub code scanning results][sarif-github], and there is a [Trivy GitHub Action][action] for automating this process +- [SonarQube][sarif-sonar] ### GitHub dependency snapshot -Trivy supports the following packages. +Trivy supports the following packages: - [OS packages][os_packages] - [Language-specific packages][language_packages] @@ -430,7 +433,10 @@ $ trivy convert --format table --severity CRITICAL result.json [cargo-auditable]: https://github.com/rust-secure-code/cargo-auditable/ [action]: https://github.com/aquasecurity/trivy-action [asff]: ../../tutorials/integrations/aws-security-hub.md -[sarif]: https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/managing-results-from-code-scanning +[sarif-home]: https://sarifweb.azurewebsites.net +[sarif-spec]: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html +[sarif-github]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning +[sarif-sonar]: https://docs.sonarsource.com/sonarqube/latest/analyzing-source-code/importing-external-issues/importing-issues-from-sarif-reports/ [sprig]: http://masterminds.github.io/sprig/ [github-sbom]: https://docs.github.com/en/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#about-dependency-submissions [github-sbom-submit]: https://docs.github.com/en/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository @@ -450,4 +456,4 @@ $ trivy convert --format table --severity CRITICAL result.json [gradle-lockfile]: ../coverage/language/java.md#gradlelock [sbt-lockfile]: ../coverage/language/java.md#sbt [pubspec-lock]: ../coverage/language/dart.md#dart -[cargo-binaries]: ../coverage/language/rust.md#binaries \ No newline at end of file +[cargo-binaries]: ../coverage/language/rust.md#binaries diff --git a/docs/docs/coverage/language/java.md b/docs/docs/coverage/language/java.md index 0ee39bbd3155..30aca897670a 100644 --- a/docs/docs/coverage/language/java.md +++ b/docs/docs/coverage/language/java.md @@ -69,6 +69,9 @@ The vulnerability database will be downloaded anyway. !!! Warning Trivy may skip some dependencies (that were not found on your local machine) when the `--offline-scan` flag is passed. +### supported scopes +Trivy only scans `import`, `compile`, `runtime` and empty [maven scopes][maven-scopes]. Other scopes and `Optional` dependencies are not currently being analyzed. + ### empty dependency version There are cases when Trivy cannot determine the version of dependencies: @@ -128,6 +131,7 @@ Make sure that you have cache[^8] directory to find licenses from `*.pom` depend [maven-invoker-plugin]: https://maven.apache.org/plugins/maven-invoker-plugin/usage.html [maven-central]: https://repo.maven.apache.org/maven2/ [maven-pom-repos]: https://maven.apache.org/settings.html#repositories +[maven-scopes]: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope [sbt-dependency-lock]: https://stringbean.github.io/sbt-dependency-lock [detection-priority]: ../../scanner/vulnerability.md#detection-priority [version-requirement]: https://maven.apache.org/pom.html#dependency-version-requirement-specification diff --git a/docs/docs/references/configuration/cli/trivy.md b/docs/docs/references/configuration/cli/trivy.md index 3d8ece9cd0e8..0f4faaecde0c 100644 --- a/docs/docs/references/configuration/cli/trivy.md +++ b/docs/docs/references/configuration/cli/trivy.md @@ -51,6 +51,7 @@ trivy [global flags] command [flags] target * [trivy kubernetes](trivy_kubernetes.md) - [EXPERIMENTAL] Scan kubernetes cluster * [trivy module](trivy_module.md) - Manage modules * [trivy plugin](trivy_plugin.md) - Manage plugins +* [trivy registry](trivy_registry.md) - Manage registry authentication * [trivy repository](trivy_repository.md) - Scan a repository * [trivy rootfs](trivy_rootfs.md) - Scan rootfs * [trivy sbom](trivy_sbom.md) - Scan SBOM for vulnerabilities and licenses diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index be7854548496..804b33725522 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -32,13 +32,14 @@ trivy config [flags] DIR --ignore-policy string specify the Rego file path to evaluate each vulnerability --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --redis-ca string redis ca file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index 0abec4990173..dd1b2cd7a2e4 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -55,7 +55,7 @@ trivy filesystem [flags] PATH --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks --include-dev-deps include development dependencies in the report (supported: npm, yarn) - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files @@ -68,6 +68,7 @@ trivy filesystem [flags] PATH --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index f9095f041572..4a76ca742cad 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -72,7 +72,7 @@ trivy image [flags] IMAGE_NAME --image-config-scanners strings comma-separated list of what security issues to detect on container image configurations (misconfig,secret) --image-src strings image source(s) to use, in priority order (docker,containerd,podman,remote) (default [docker,containerd,podman,remote]) --include-deprecated-checks include deprecated checks - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --input string input file path instead of image name --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) @@ -86,6 +86,7 @@ trivy image [flags] IMAGE_NAME --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --platform string set platform in the form os/arch if image is multi-platform capable diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 99fbc1badaa2..1a25e859c3c8 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -69,7 +69,7 @@ trivy kubernetes [flags] [CONTEXT] --include-deprecated-checks include deprecated checks --include-kinds strings indicate the kinds included in scanning (example: node) --include-namespaces strings indicate the namespaces included in scanning (example: kube-system) - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --kubeconfig string specify the kubeconfig file path to use @@ -83,6 +83,7 @@ trivy kubernetes [flags] [CONTEXT] --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --qps float specify the maximum QPS to the master from this client (default 5) diff --git a/docs/docs/references/configuration/cli/trivy_registry.md b/docs/docs/references/configuration/cli/trivy_registry.md new file mode 100644 index 000000000000..4a42cea3f4df --- /dev/null +++ b/docs/docs/references/configuration/cli/trivy_registry.md @@ -0,0 +1,29 @@ +## trivy registry + +Manage registry authentication + +### Options + +``` + -h, --help help for registry +``` + +### Options inherited from parent commands + +``` + --cache-dir string cache directory (default "/path/to/cache") + -c, --config string config path (default "trivy.yaml") + -d, --debug debug mode + --generate-default-config write the default config to trivy-default.yaml + --insecure allow insecure server connections + -q, --quiet suppress progress bar and log output + --timeout duration timeout (default 5m0s) + -v, --version show version +``` + +### SEE ALSO + +* [trivy](trivy.md) - Unified security scanner +* [trivy registry login](trivy_registry_login.md) - Log in to a registry +* [trivy registry logout](trivy_registry_logout.md) - Log out of a registry + diff --git a/docs/docs/references/configuration/cli/trivy_registry_login.md b/docs/docs/references/configuration/cli/trivy_registry_login.md new file mode 100644 index 000000000000..6e963b3ca9f4 --- /dev/null +++ b/docs/docs/references/configuration/cli/trivy_registry_login.md @@ -0,0 +1,41 @@ +## trivy registry login + +Log in to a registry + +``` +trivy registry login SERVER [flags] +``` + +### Examples + +``` + # Log in to reg.example.com + cat ~/my_password.txt | trivy registry login --username foo --password-stdin reg.example.com +``` + +### Options + +``` + -h, --help help for login + --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. + --username strings username. Comma-separated usernames allowed. +``` + +### Options inherited from parent commands + +``` + --cache-dir string cache directory (default "/path/to/cache") + -c, --config string config path (default "trivy.yaml") + -d, --debug debug mode + --generate-default-config write the default config to trivy-default.yaml + --insecure allow insecure server connections + -q, --quiet suppress progress bar and log output + --timeout duration timeout (default 5m0s) + -v, --version show version +``` + +### SEE ALSO + +* [trivy registry](trivy_registry.md) - Manage registry authentication + diff --git a/docs/docs/references/configuration/cli/trivy_registry_logout.md b/docs/docs/references/configuration/cli/trivy_registry_logout.md new file mode 100644 index 000000000000..b3da44c5fe92 --- /dev/null +++ b/docs/docs/references/configuration/cli/trivy_registry_logout.md @@ -0,0 +1,38 @@ +## trivy registry logout + +Log out of a registry + +``` +trivy registry logout SERVER [flags] +``` + +### Examples + +``` + # Log out of reg.example.com + trivy registry logout reg.example.com +``` + +### Options + +``` + -h, --help help for logout +``` + +### Options inherited from parent commands + +``` + --cache-dir string cache directory (default "/path/to/cache") + -c, --config string config path (default "trivy.yaml") + -d, --debug debug mode + --generate-default-config write the default config to trivy-default.yaml + --insecure allow insecure server connections + -q, --quiet suppress progress bar and log output + --timeout duration timeout (default 5m0s) + -v, --version show version +``` + +### SEE ALSO + +* [trivy registry](trivy_registry.md) - Manage registry authentication + diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 821923262dee..5936e0c34a61 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -55,7 +55,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks --include-dev-deps include development dependencies in the report (supported: npm, yarn) - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files @@ -68,6 +68,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index bf2575af23b9..acd1d529a8ef 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -57,7 +57,7 @@ trivy rootfs [flags] ROOTDIR --ignored-licenses strings specify a list of license to ignore --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files @@ -70,6 +70,7 @@ trivy rootfs [flags] ROOTDIR --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index f09453845518..55df0959deb6 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -47,12 +47,15 @@ trivy sbom [flags] SBOM_PATH --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments + --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend --redis-tls enable redis TLS with public certificates, if using redis as cache backend + --registry-token string registry token --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln]) @@ -67,6 +70,7 @@ trivy sbom [flags] SBOM_PATH -t, --template string output template --token string for authentication in client/server mode --token-header string specify a header name for token in client/server mode (default "Trivy-Token") + --username strings username. Comma-separated usernames allowed. --vex strings [EXPERIMENTAL] VEX sources ("repo", "oci" or file path) ``` diff --git a/docs/docs/references/configuration/cli/trivy_server.md b/docs/docs/references/configuration/cli/trivy_server.md index b7ada8f2c19b..80eacd43ee4f 100644 --- a/docs/docs/references/configuration/cli/trivy_server.md +++ b/docs/docs/references/configuration/cli/trivy_server.md @@ -30,6 +30,7 @@ trivy server [flags] --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --redis-ca string redis ca file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 14ce8b78c21a..d4e3692f7e1c 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -51,7 +51,7 @@ trivy vm [flags] VM_IMAGE --ignore-status strings comma-separated list of vulnerability status to ignore (unknown,not_affected,affected,fixed,under_investigation,will_not_fix,fix_deferred,end_of_life) --ignore-unfixed display only fixed vulnerabilities --ignorefile string specify .trivyignore file (default ".trivyignore") - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --list-all-pkgs output all packages in the JSON report regardless of vulnerability --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index 9bc9d4351360..ae04978801b3 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -461,6 +461,9 @@ registry: # Same as '--password' password: [] + # Same as '--password-stdin' + password-stdin: false + # Same as '--registry-token' token: "" diff --git a/docs/docs/scanner/misconfiguration/check/exceptions.md b/docs/docs/scanner/misconfiguration/check/exceptions.md deleted file mode 100644 index 70ef974a4b74..000000000000 --- a/docs/docs/scanner/misconfiguration/check/exceptions.md +++ /dev/null @@ -1,92 +0,0 @@ -# Exceptions -Exceptions let you specify cases where you allow policy violations. -Trivy supports two types of exceptions. - -!!! info - Exceptions can be applied to built-in checks as well as custom checks. - -## Namespace-based exceptions -There are some cases where you need to disable built-in checks partially or fully. -Namespace-based exceptions lets you rough choose which individual packages to exempt. - -To use namespace-based exceptions, create a Rego rule with the name `exception` that returns the package names to exempt. -The `exception` rule must be defined under `namespace.exceptions`. -`data.namespaces` includes all package names. - - -!!! example - ``` rego - package namespace.exceptions - - import data.namespaces - - exception[ns] { - ns := data.namespaces[_] - startswith(ns, "builtin.kubernetes") - } - ``` - -This example exempts all built-in checks for Kubernetes. - -## Rule-based exceptions -There are some cases where you need more flexibility and granularity in defining which cases to exempt. -Rule-based exceptions lets you granularly choose which individual rules to exempt, while also declaring under which conditions to exempt them. - -To use rule-based exceptions, create a Rego rule with the name `exception` that returns the rule name suffixes to exempt, prefixed by `deny_` (for example, returning `foo` will exempt `deny_foo`). -The rule can make any other assertion, for example, on the input or data documents. -This is useful to specify the exemption for a specific case. - -Note that if you specify the empty string, the exception will match all rules named `deny`. - -``` -exception[rules] { - # Logic - - rules = ["foo","bar"] -} -``` - -The above would provide an exception from `deny_foo` and `deny_bar`. - - -!!! example - ``` - package user.kubernetes.ID100 - - __rego_metadata := { - "id": "ID100", - "title": "Deployment not allowed", - "severity": "HIGH", - "type": "Kubernetes Custom Check", - } - - deny_deployment[msg] { - input.kind == "Deployment" - msg = sprintf("Found deployment '%s' but deployments are not allowed", [name]) - } - - exception[rules] { - input.kind == "Deployment" - input.metadata.name == "allow-deployment" - - rules := ["deployment"] - } - ``` - -If you want to apply rule-based exceptions to built-in checks, you have to define the exception under the same package. - -!!! example - ``` rego - package builtin.kubernetes.KSV012 - - exception[rules] { - input.metadata.name == "can-run-as-root" - rules := [""] - } - ``` - -This exception is applied to [KSV012][ksv012] in trivy-checks. -You can get the package names in the [trivy-checks repository][trivy-checks] or the JSON output from Trivy. - -[ksv012]: https://github.com/aquasecurity/trivy-checks/blob/f36a5b732c4b1293a720c40baab0a7c106ea455e/checks/kubernetes/pss/restricted/3_runs_as_root.rego -[trivy-checks]: https://github.com/aquasecurity/trivy-checks/ \ No newline at end of file diff --git a/docs/docs/scanner/misconfiguration/custom/debug.md b/docs/docs/scanner/misconfiguration/custom/debug.md index 54ec9fd65273..bb7b506c7ece 100644 --- a/docs/docs/scanner/misconfiguration/custom/debug.md +++ b/docs/docs/scanner/misconfiguration/custom/debug.md @@ -12,7 +12,7 @@ $ trivy config --trace configs/ Dockerfile (dockerfile) ======================= -Tests: 23 (SUCCESSES: 21, FAILURES: 2, EXCEPTIONS: 0) +Tests: 23 (SUCCESSES: 21, FAILURES: 2) Failures: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 1, CRITICAL: 0) MEDIUM: Specify a tag in the 'FROM' statement for image 'alpine' diff --git a/docs/docs/scanner/misconfiguration/custom/index.md b/docs/docs/scanner/misconfiguration/custom/index.md index 9598089b8562..d45638f1533d 100644 --- a/docs/docs/scanner/misconfiguration/custom/index.md +++ b/docs/docs/scanner/misconfiguration/custom/index.md @@ -163,7 +163,7 @@ Some fields are displayed in scan results. k.yaml (kubernetes) ─────────────────── -Tests: 32 (SUCCESSES: 31, FAILURES: 1, EXCEPTIONS: 0) +Tests: 32 (SUCCESSES: 31, FAILURES: 1) Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0) LOW: Found deployment 'my-deployment' but deployments are not allowed diff --git a/docs/docs/scanner/misconfiguration/index.md b/docs/docs/scanner/misconfiguration/index.md index 368b4522b433..8a2f331b6ab0 100644 --- a/docs/docs/scanner/misconfiguration/index.md +++ b/docs/docs/scanner/misconfiguration/index.md @@ -20,7 +20,7 @@ $ trivy config [YOUR_IaC_DIRECTORY] Dockerfile (dockerfile) ======================= - Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0) + Tests: 23 (SUCCESSES: 22, FAILURES: 1) Failures: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0) MEDIUM: Specify a tag in the 'FROM' statement for image 'alpine' @@ -75,7 +75,7 @@ You can specify `--scanners vuln,misconfig,secret` to enable vulnerability and s Dockerfile (dockerfile) ======================= - Tests: 17 (SUCCESSES: 16, FAILURES: 1, EXCEPTIONS: 0) + Tests: 17 (SUCCESSES: 16, FAILURES: 1) Failures: 1 (HIGH: 1, CRITICAL: 0) HIGH: Last USER command in Dockerfile should not be 'root' @@ -112,7 +112,7 @@ $ trivy config --severity HIGH,CRITICAL ./iac Dockerfile (dockerfile) -Tests: 21 (SUCCESSES: 20, FAILURES: 1, EXCEPTIONS: 0) +Tests: 21 (SUCCESSES: 20, FAILURES: 1) Failures: 1 (MEDIUM: 0, HIGH: 1, CRITICAL: 0) HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument @@ -126,7 +126,7 @@ See https://avd.aquasec.com/misconfig/ds002 deployment.yaml (kubernetes) -Tests: 20 (SUCCESSES: 15, FAILURES: 5, EXCEPTIONS: 0) +Tests: 20 (SUCCESSES: 15, FAILURES: 5) Failures: 5 (MEDIUM: 4, HIGH: 1, CRITICAL: 0) MEDIUM: Container 'hello-kubernetes' of Deployment 'hello-kubernetes' should set 'securityContext.allowPrivilegeEscalation' to false @@ -225,7 +225,7 @@ See https://avd.aquasec.com/misconfig/ksv026 mysql-8.8.26.tar:templates/primary/statefulset.yaml (helm) -Tests: 20 (SUCCESSES: 18, FAILURES: 2, EXCEPTIONS: 0) +Tests: 20 (SUCCESSES: 18, FAILURES: 2) Failures: 2 (MEDIUM: 2, HIGH: 0, CRITICAL: 0) MEDIUM: Container 'mysql' of StatefulSet 'mysql' should set 'securityContext.allowPrivilegeEscalation' to false @@ -279,35 +279,35 @@ You can see the config type next to each file name. ``` bash Dockerfile (dockerfile) ======================= -Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0) +Tests: 23 (SUCCESSES: 22, FAILURES: 1) Failures: 1 (HIGH: 1, CRITICAL: 0) ... deployment.yaml (kubernetes) ============================ -Tests: 28 (SUCCESSES: 15, FAILURES: 13, EXCEPTIONS: 0) +Tests: 28 (SUCCESSES: 15, FAILURES: 13) Failures: 13 (MEDIUM: 4, HIGH: 1, CRITICAL: 0) ... main.tf (terraform) =================== -Tests: 23 (SUCCESSES: 14, FAILURES: 9, EXCEPTIONS: 0) +Tests: 23 (SUCCESSES: 14, FAILURES: 9) Failures: 9 (HIGH: 6, CRITICAL: 1) ... bucket.yaml (cloudformation) ============================ -Tests: 9 (SUCCESSES: 3, FAILURES: 6, EXCEPTIONS: 0) +Tests: 9 (SUCCESSES: 3, FAILURES: 6) Failures: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 4, CRITICAL: 0) ... mysql-8.8.26.tar:templates/primary/statefulset.yaml (helm) ========================================================== -Tests: 20 (SUCCESSES: 18, FAILURES: 2, EXCEPTIONS: 0) +Tests: 20 (SUCCESSES: 18, FAILURES: 2) Failures: 2 (MEDIUM: 2, HIGH: 0, CRITICAL: 0) ``` @@ -381,7 +381,7 @@ deny[res] { $ trivy config --misconfig-scanners=json,yaml --config-check ./serverless.rego --check-namespaces user ./iac serverless.yaml (yaml) -Tests: 4 (SUCCESSES: 3, FAILURES: 1, EXCEPTIONS: 0) +Tests: 4 (SUCCESSES: 3, FAILURES: 1) Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0) LOW: Service name "serverless-rest-api-with-pynamodb" is not allowed diff --git a/docs/docs/supply-chain/vex/file.md b/docs/docs/supply-chain/vex/file.md index 7c847ec49e14..242ec4c5a040 100644 --- a/docs/docs/supply-chain/vex/file.md +++ b/docs/docs/supply-chain/vex/file.md @@ -64,7 +64,7 @@ $ cat < trivy.vex.cdx }, "affects": [ { - "ref": "urn:cdx:3e671687-395b-41f5-a30f-a58921a69b79/1#pkg:golang/github.com/aws/aws-sdk-go@1.44.234" + "ref": "urn:cdx:3e671687-395b-41f5-a30f-a58921a69b79/1#pkg:golang/github.com/aws/aws-sdk-go@v1.44.234" } ] } @@ -115,7 +115,7 @@ Total: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0) ┌───────────────────────────┬───────────────┬──────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │ ├───────────────────────────┼───────────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────────┤ -│ github.com/aws/aws-sdk-go │ CVE-2020-8912 │ LOW │ 1.44.234 │ │ aws-sdk-go: In-band key negotiation issue in AWS S3 Crypto │ +│ github.com/aws/aws-sdk-go │ CVE-2020-8912 │ LOW │ v1.44.234 │ │ aws-sdk-go: In-band key negotiation issue in AWS S3 Crypto │ │ │ │ │ │ │ SDK for golang... │ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-8912 │ └───────────────────────────┴───────────────┴──────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────────┘ @@ -497,9 +497,9 @@ Now, suppose a VEX statement is issued for `Module B` as follows: "vulnerability": {"name": "CVE-XXXX-YYYY"}, "products": [ { - "@id": "pkg:golang/module-b@1.0.0", + "@id": "pkg:golang/module-b@v1.0.0", "subcomponents": [ - { "@id": "pkg:golang/module-c@2.0.0" } + { "@id": "pkg:golang/module-c@v2.0.0" } ] } ], diff --git a/docs/docs/target/container_image.md b/docs/docs/target/container_image.md index 94acc954a743..274b43862339 100644 --- a/docs/docs/target/container_image.md +++ b/docs/docs/target/container_image.md @@ -119,7 +119,7 @@ $ trivy image --image-config-scanners misconfig [YOUR_IMAGE_NAME] ``` alpine:3.17 (dockerfile) ======================== -Tests: 24 (SUCCESSES: 21, FAILURES: 3, EXCEPTIONS: 0) +Tests: 24 (SUCCESSES: 21, FAILURES: 3) Failures: 3 (UNKNOWN: 0, LOW: 2, MEDIUM: 0, HIGH: 1, CRITICAL: 0) HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument @@ -154,6 +154,8 @@ See https://avd.aquasec.com/misconfig/ds026 !!! tip You can see how each layer is created with `docker history`. +The [AVD-DS-0016](https://avd.aquasec.com/misconfig/dockerfile/general/avd-ds-0016/) check is disabled for this scan type, see [issue](https://github.com/aquasecurity/trivy/issues/7368) for details. + ### Secrets Trivy detects secrets on the configuration of container images. The image config is converted into JSON and Trivy scans the file for secrets. @@ -297,7 +299,7 @@ Trivy supports registries that comply with the following specifications. - [Docker Registry HTTP API V2](https://docs.docker.com/registry/spec/api/) - [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec) -You can configure credentials with `docker login`. +You can configure credentials with `trivy registry login`. See [here](../advanced/private-registries/index.md) for the detail. ### Tar Files diff --git a/docs/docs/target/kubernetes.md b/docs/docs/target/kubernetes.md index 10253aff4c72..291c9fb7728e 100644 --- a/docs/docs/target/kubernetes.md +++ b/docs/docs/target/kubernetes.md @@ -280,8 +280,7 @@ trivy k8s --format json -o results.json cluster "Type": "kubernetes", "MisconfSummary": { "Successes": 20, - "Failures": 19, - "Exceptions": 0 + "Failures": 19 }, "Misconfigurations": [ { diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 61f6e42871bb..6e3c8cd0cfd2 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -34,7 +34,7 @@ In this section you will find an aggregation of the different ways to install Tr Add repository setting to `/etc/apt/sources.list.d`. ``` bash - sudo apt-get install wget apt-transport-https gnupg + sudo apt-get install wget gnupg wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list sudo apt-get update @@ -56,6 +56,13 @@ Homebrew for MacOS and Linux. brew install trivy ``` +### Windows (Official) + +1. Download trivy_x.xx.x_windows-64bit.zip file from [releases page](https://github.com/aquasecurity/trivy/releases/). +2. Unzip file and copy to any folder. +3. Ensure PATH environment variable is configured to folder trivy installed. + + ### Arch Linux (Community) Arch Linux Package Repository. diff --git a/contrib/example_policy/advanced.rego b/examples/ignore-policies/advanced.rego similarity index 100% rename from contrib/example_policy/advanced.rego rename to examples/ignore-policies/advanced.rego diff --git a/contrib/example_policy/basic.rego b/examples/ignore-policies/basic.rego similarity index 100% rename from contrib/example_policy/basic.rego rename to examples/ignore-policies/basic.rego diff --git a/examples/ignore-policies/whitelist.rego b/examples/ignore-policies/whitelist.rego new file mode 100644 index 000000000000..51a75c37c72f --- /dev/null +++ b/examples/ignore-policies/whitelist.rego @@ -0,0 +1,13 @@ +package trivy + +import rego.v1 + +allowed_checks := { + "AVD-AWS-0089" +} + +default ignore := false + +ignore if not is_check_allowed + +is_check_allowed if input.AVDID in allowed_checks \ No newline at end of file diff --git a/go.mod b/go.mod index d01ce2a7d4c7..f4a034a8063a 100644 --- a/go.mod +++ b/go.mod @@ -25,10 +25,10 @@ require ( github.com/aquasecurity/table v1.8.0 github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 github.com/aquasecurity/tml v0.6.1 - github.com/aquasecurity/trivy-checks v1.1.0 + github.com/aquasecurity/trivy-checks v1.2.2 github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 - github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240707095038-0300bc49b68b + github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4 github.com/aws/aws-sdk-go-v2 v1.31.0 github.com/aws/aws-sdk-go-v2/config v1.27.38 github.com/aws/aws-sdk-go-v2/credentials v1.17.36 @@ -43,6 +43,7 @@ require ( github.com/cheggaaa/pb/v3 v3.1.5 github.com/containerd/containerd v1.7.22 github.com/csaf-poc/csaf_distribution/v3 v3.0.0 + github.com/docker/cli v27.2.1+incompatible github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 github.com/fatih/color v1.17.0 @@ -123,14 +124,14 @@ require ( golang.org/x/mod v0.21.0 golang.org/x/net v0.29.0 golang.org/x/sync v0.8.0 - golang.org/x/term v0.24.0 + golang.org/x/term v0.25.0 golang.org/x/text v0.18.0 golang.org/x/vuln v1.1.3 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.1 - k8s.io/api v0.31.0 + k8s.io/api v0.31.2 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 modernc.org/sqlite v1.33.1 sigs.k8s.io/yaml v1.4.0 @@ -172,7 +173,7 @@ require ( github.com/antchfx/xpath v1.3.1 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.54.6 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect @@ -210,7 +211,6 @@ require ( github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect - github.com/docker/cli v27.2.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect @@ -385,7 +385,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/sys v0.26.0 // indirect golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.24.0 // indirect @@ -402,21 +402,21 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect - k8s.io/apimachinery v0.31.0 // indirect + k8s.io/apimachinery v0.31.2 // indirect k8s.io/apiserver v0.31.0 // indirect - k8s.io/cli-runtime v0.31.0 // indirect - k8s.io/client-go v0.31.0 // indirect - k8s.io/component-base v0.31.0 // indirect + k8s.io/cli-runtime v0.31.2 // indirect + k8s.io/client-go v0.31.2 // indirect + k8s.io/component-base v0.31.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/kubectl v0.31.0 // indirect + k8s.io/kubectl v0.31.2 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect - mvdan.cc/sh/v3 v3.8.0 // indirect + mvdan.cc/sh/v3 v3.10.0 // indirect oras.land/oras-go v1.2.5 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.17.2 // indirect diff --git a/go.sum b/go.sum index 51d43bf95aa1..5eadfbf83818 100644 --- a/go.sum +++ b/go.sum @@ -349,14 +349,14 @@ github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 h1:b43UVqY github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8/go.mod h1:wXA9k3uuaxY3yu7gxrxZDPo/04FEMJtwyecdAlYrEIo= github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gwo= github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY= -github.com/aquasecurity/trivy-checks v1.1.0 h1:I0tVOK8dG/KHrWsqfGNYp2uD/i0f+yS7Je31F+LIUqQ= -github.com/aquasecurity/trivy-checks v1.1.0/go.mod h1:tVzhU0gajD3GmxKPLn/BHR8ZeUquc5ajQTmAsi0kCCU= +github.com/aquasecurity/trivy-checks v1.2.2 h1:EVHi0gthYzDLfqdAqBBwVGfg2l/gdZ622pIlC9rP+lU= +github.com/aquasecurity/trivy-checks v1.2.2/go.mod h1:TNV0QNVFyBIkt865eO2PtfpubmHt3Ve19Klny//SWIU= github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 h1:G0gnacAORRUqz2Tm5MqivSpldY2GZ74ijhJcMsae+sA= github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1/go.mod h1:PYkSRx4dlgFATEt+okGwibvbxVEtqsOdH+vX/saACYE= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8= -github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240707095038-0300bc49b68b h1:h7gsIzHyrxpQnayOuQI0kX7+8rVcqhV6G5bM3KVFyJU= -github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240707095038-0300bc49b68b/go.mod h1:HOhrqoyIeTxpwnKr1EyWtQ+rt2XahV8b0UDBrRpSfEQ= +github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4 h1:i0Z0JS4xtMAcBVOpYSciS7slmIBi1SmjT6garbrJtcA= +github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4/go.mod h1:ctlibFXOQyjWybeVVQI6NLG6GJoPWZJ4cIirQ/wPCQs= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -364,8 +364,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.54.6 h1:HEYUib3yTt8E6vxjMWM3yAq5b+qjj/6aKA62mkgux9g= -github.com/aws/aws-sdk-go v1.54.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= github.com/aws/aws-sdk-go-v2/config v1.27.38 h1:mMVyJJuSUdbD4zKXoxDgWrgM60QwlFEg+JhihCq6wCw= @@ -510,8 +510,8 @@ github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHf github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= +github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= 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/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc= @@ -683,6 +683,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= 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.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= @@ -1204,8 +1206,8 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI= github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1703,8 +1705,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0= golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1715,8 +1717,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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= @@ -2076,26 +2078,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= -k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= -k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= -k8s.io/cli-runtime v0.31.0 h1:V2Q1gj1u3/WfhD475HBQrIYsoryg/LrhhK4RwpN+DhA= -k8s.io/cli-runtime v0.31.0/go.mod h1:vg3H94wsubuvWfSmStDbekvbla5vFGC+zLWqcf+bGDw= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= -k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= -k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= +k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= +k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.31.0 h1:kANwAAPVY02r4U4jARP/C+Q1sssCcN/1p9Nk+7BQKVg= -k8s.io/kubectl v0.31.0/go.mod h1:pB47hhFypGsaHAPjlwrNbvhXgmuAr01ZBvAIIUaI8d4= +k8s.io/kubectl v0.31.2 h1:gTxbvRkMBwvTSAlobiTVqsH6S8Aa1aGyBcu5xYLsn8M= +k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= @@ -2124,8 +2126,8 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -mvdan.cc/sh/v3 v3.8.0 h1:ZxuJipLZwr/HLbASonmXtcvvC9HXY9d2lXZHnKGjFc8= -mvdan.cc/sh/v3 v3.8.0/go.mod h1:w04623xkgBVo7/IUK89E0g8hBykgEpN0vgOj3RJr6MY= +mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= +mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/integration/client_server_test.go b/integration/client_server_test.go index 07a4f086200c..4afed3fdc155 100644 --- a/integration/client_server_test.go +++ b/integration/client_server_test.go @@ -323,6 +323,17 @@ func TestClientServerWithFormat(t *testing.T) { }, golden: "testdata/alpine-310.gitlab.golden", }, + { + name: "scan package-lock.json with gitlab template (Unknown os and image)", + args: csArgs{ + Command: "fs", + Format: "template", + TemplatePath: "@../contrib/gitlab.tpl", + Target: "testdata/fixtures/repo/npm/", + ListAllPackages: true, + }, + golden: "testdata/npm.gitlab.golden", + }, { name: "alpine 3.10 with gitlab-codequality template", args: csArgs{ diff --git a/integration/registry_test.go b/integration/registry_test.go index 3831ea9a280e..25ddaf5b2814 100644 --- a/integration/registry_test.go +++ b/integration/registry_test.go @@ -117,6 +117,7 @@ type registryOption struct { Username string Password string RegistryToken bool + AuthLogin bool } func TestRegistry(t *testing.T) { @@ -164,7 +165,6 @@ func TestRegistry(t *testing.T) { imageFile: "testdata/fixtures/images/alpine-310.tar.gz", os: "alpine 3.10.2", option: registryOption{ - AuthURL: authURL, Username: authUsername, Password: authPassword, }, @@ -183,13 +183,24 @@ func TestRegistry(t *testing.T) { }, golden: "testdata/alpine-310.json.golden", }, + { + name: "authenticate with 'trivy registry login'", + imageName: "alpine:3.10", + imageFile: "testdata/fixtures/images/alpine-310.tar.gz", + os: "alpine 3.10.2", + option: registryOption{ + Username: authUsername, + Password: authPassword, + AuthLogin: true, + }, + golden: "testdata/alpine-310.json.golden", + }, { name: "amazonlinux 2", imageName: "amazonlinux:2", imageFile: "testdata/fixtures/images/amazon-2.tar.gz", os: "amazon 2 (Karoo)", option: registryOption{ - AuthURL: authURL, Username: authUsername, Password: authPassword, }, @@ -201,7 +212,6 @@ func TestRegistry(t *testing.T) { imageFile: "testdata/fixtures/images/debian-buster.tar.gz", os: "debian 10.1", option: registryOption{ - AuthURL: authURL, Username: authUsername, Password: authPassword, }, @@ -226,6 +236,7 @@ func TestRegistry(t *testing.T) { require.NoError(t, err) osArgs, err := scan(t, imageRef, baseDir, tt.option) + require.NoError(t, err) // Run Trivy runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{ @@ -262,7 +273,7 @@ func scan(t *testing.T, imageRef name.Reference, baseDir string, opt registryOpt "json", "--image-src", "remote", - "--skip-update", + "--skip-db-update", imageRef.Name(), } @@ -273,14 +284,30 @@ func setupEnv(t *testing.T, imageRef name.Reference, baseDir string, opt registr t.Setenv("TRIVY_INSECURE", "true") if opt.Username != "" && opt.Password != "" { - if opt.RegistryToken { + switch { + case opt.RegistryToken: // Get a registry token in advance token, err := requestRegistryToken(imageRef, baseDir, opt) if err != nil { return err } t.Setenv("TRIVY_REGISTRY_TOKEN", token) - } else { + case opt.AuthLogin: + t.Setenv("DOCKER_CONFIG", t.TempDir()) + err := execute([]string{ + "registry", + "login", + "--username", + opt.Username, + "--password", + opt.Password, + "--insecure", + imageRef.Context().RegistryStr(), + }) + if err != nil { + return err + } + default: t.Setenv("TRIVY_USERNAME", opt.Username) t.Setenv("TRIVY_PASSWORD", opt.Password) } @@ -307,7 +334,7 @@ func requestRegistryToken(imageRef name.Reference, baseDir string, opt registryO } // Get a registry token - req, err := http.NewRequest("GET", fmt.Sprintf("%s/auth", opt.AuthURL), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/auth", opt.AuthURL), nil) if err != nil { return "", err } diff --git a/integration/repo_test.go b/integration/repo_test.go index 69833bcf2b77..87c27684d3ab 100644 --- a/integration/repo_test.go +++ b/integration/repo_test.go @@ -300,24 +300,6 @@ func TestRepository(t *testing.T) { }, golden: "testdata/dockerfile_file_pattern.json.golden", }, - { - name: "dockerfile with rule exception", - args: args{ - scanner: types.MisconfigScanner, - policyPaths: []string{"testdata/fixtures/repo/rule-exception/policy"}, - input: "testdata/fixtures/repo/rule-exception", - }, - golden: "testdata/dockerfile-rule-exception.json.golden", - }, - { - name: "dockerfile with namespace exception", - args: args{ - scanner: types.MisconfigScanner, - policyPaths: []string{"testdata/fixtures/repo/namespace-exception/policy"}, - input: "testdata/fixtures/repo/namespace-exception", - }, - golden: "testdata/dockerfile-namespace-exception.json.golden", - }, { name: "dockerfile with custom policies", args: args{ diff --git a/integration/testdata/alpine-310.gitlab.golden b/integration/testdata/alpine-310.gitlab.golden index ad769b31959e..a0339dcf6bfb 100644 --- a/integration/testdata/alpine-310.gitlab.golden +++ b/integration/testdata/alpine-310.gitlab.golden @@ -37,7 +37,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ @@ -104,7 +104,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ @@ -191,7 +191,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ @@ -258,7 +258,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ diff --git a/integration/testdata/conda-cyclonedx.json.golden b/integration/testdata/conda-cyclonedx.json.golden index 7f3a352fcce7..df8f575e9053 100644 --- a/integration/testdata/conda-cyclonedx.json.golden +++ b/integration/testdata/conda-cyclonedx.json.golden @@ -34,6 +34,12 @@ "type": "library", "name": "openssl", "version": "1.1.1q", + "hashes": [ + { + "alg": "SHA-1", + "content": "237db0da53131e4548cb1181337fa0f420299e1f" + } + ], "licenses": [ { "license": { @@ -58,6 +64,12 @@ "type": "library", "name": "pip", "version": "22.2.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "a6a2db7668f1ad541d704369fc66c96a4415aa24" + } + ], "licenses": [ { "license": { diff --git a/integration/testdata/conda-spdx.json.golden b/integration/testdata/conda-spdx.json.golden index c14d1bf4e018..c11f52eeca1b 100644 --- a/integration/testdata/conda-spdx.json.golden +++ b/integration/testdata/conda-spdx.json.golden @@ -31,10 +31,15 @@ "referenceLocator": "pkg:conda/openssl@1.1.1q" } ], - "attributionTexts": [ - "PkgType: conda-pkg" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: conda-pkg" + } + ] }, { "name": "pip", @@ -55,20 +60,30 @@ "referenceLocator": "pkg:conda/pip@22.2.2" } ], - "attributionTexts": [ - "PkgType: conda-pkg" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: conda-pkg" + } + ] }, { "name": "testdata/fixtures/repo/conda", "SPDXID": "SPDXRef-Filesystem-2e2426fd0f2580ef", "downloadLocation": "NONE", "filesAnalyzed": false, - "attributionTexts": [ - "SchemaVersion: 2" - ], - "primaryPackagePurpose": "SOURCE" + "primaryPackagePurpose": "SOURCE", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "SchemaVersion: 2" + } + ] } ], "files": [ diff --git a/integration/testdata/dockerfile-custom-policies.json.golden b/integration/testdata/dockerfile-custom-policies.json.golden index 438ecf5d7270..64ce52ba64b3 100644 --- a/integration/testdata/dockerfile-custom-policies.json.golden +++ b/integration/testdata/dockerfile-custom-policies.json.golden @@ -21,9 +21,8 @@ "Class": "config", "Type": "dockerfile", "MisconfSummary": { - "Successes": 27, - "Failures": 2, - "Exceptions": 0 + "Successes": 28, + "Failures": 2 }, "Misconfigurations": [ { diff --git a/integration/testdata/dockerfile-namespace-exception.json.golden b/integration/testdata/dockerfile-namespace-exception.json.golden deleted file mode 100644 index be3a6adfa8d6..000000000000 --- a/integration/testdata/dockerfile-namespace-exception.json.golden +++ /dev/null @@ -1,30 +0,0 @@ -{ - "SchemaVersion": 2, - "CreatedAt": "2021-08-25T12:20:30.000000005Z", - "ArtifactName": "testdata/fixtures/repo/namespace-exception", - "ArtifactType": "repository", - "Metadata": { - "ImageConfig": { - "architecture": "", - "created": "0001-01-01T00:00:00Z", - "os": "", - "rootfs": { - "type": "", - "diff_ids": null - }, - "config": {} - } - }, - "Results": [ - { - "Target": "Dockerfile", - "Class": "config", - "Type": "dockerfile", - "MisconfSummary": { - "Successes": 0, - "Failures": 0, - "Exceptions": 27 - } - } - ] -} diff --git a/integration/testdata/dockerfile-rule-exception.json.golden b/integration/testdata/dockerfile-rule-exception.json.golden deleted file mode 100644 index eae4e1e32fd6..000000000000 --- a/integration/testdata/dockerfile-rule-exception.json.golden +++ /dev/null @@ -1,58 +0,0 @@ -{ - "SchemaVersion": 2, - "CreatedAt": "2021-08-25T12:20:30.000000005Z", - "ArtifactName": "testdata/fixtures/repo/rule-exception", - "ArtifactType": "repository", - "Metadata": { - "ImageConfig": { - "architecture": "", - "created": "0001-01-01T00:00:00Z", - "os": "", - "rootfs": { - "type": "", - "diff_ids": null - }, - "config": {} - } - }, - "Results": [ - { - "Target": "Dockerfile", - "Class": "config", - "Type": "dockerfile", - "MisconfSummary": { - "Successes": 26, - "Failures": 1, - "Exceptions": 0 - }, - "Misconfigurations": [ - { - "Type": "Dockerfile Security Check", - "ID": "DS002", - "AVDID": "AVD-DS-0002", - "Title": "Image user should not be 'root'", - "Description": "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.", - "Message": "Specify at least 1 USER command in Dockerfile with non-root user as argument", - "Namespace": "builtin.dockerfile.DS002", - "Query": "data.builtin.dockerfile.DS002.deny", - "Resolution": "Add 'USER \u003cnon root user name\u003e' line to the Dockerfile", - "Severity": "HIGH", - "PrimaryURL": "https://avd.aquasec.com/misconfig/ds002", - "References": [ - "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", - "https://avd.aquasec.com/misconfig/ds002" - ], - "Status": "FAIL", - "Layer": {}, - "CauseMetadata": { - "Provider": "Dockerfile", - "Service": "general", - "Code": { - "Lines": null - } - } - } - ] - } - ] -} diff --git a/integration/testdata/dockerfile.json.golden b/integration/testdata/dockerfile.json.golden index 3f6dd1b5b201..fd1fcbfceec8 100644 --- a/integration/testdata/dockerfile.json.golden +++ b/integration/testdata/dockerfile.json.golden @@ -21,9 +21,8 @@ "Class": "config", "Type": "dockerfile", "MisconfSummary": { - "Successes": 26, - "Failures": 1, - "Exceptions": 0 + "Successes": 27, + "Failures": 1 }, "Misconfigurations": [ { diff --git a/integration/testdata/dockerfile_file_pattern.json.golden b/integration/testdata/dockerfile_file_pattern.json.golden index fe0372ddc79c..adcd916456d5 100644 --- a/integration/testdata/dockerfile_file_pattern.json.golden +++ b/integration/testdata/dockerfile_file_pattern.json.golden @@ -21,9 +21,8 @@ "Class": "config", "Type": "dockerfile", "MisconfSummary": { - "Successes": 26, - "Failures": 1, - "Exceptions": 0 + "Successes": 27, + "Failures": 1 }, "Misconfigurations": [ { diff --git a/integration/testdata/fixtures/repo/namespace-exception/Dockerfile b/integration/testdata/fixtures/repo/namespace-exception/Dockerfile deleted file mode 100644 index fa4b7580b688..000000000000 --- a/integration/testdata/fixtures/repo/namespace-exception/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM alpine:3.13 -LABEL user.root="allow" diff --git a/integration/testdata/fixtures/repo/namespace-exception/policy/exception.rego b/integration/testdata/fixtures/repo/namespace-exception/policy/exception.rego deleted file mode 100644 index 74abdccb1933..000000000000 --- a/integration/testdata/fixtures/repo/namespace-exception/policy/exception.rego +++ /dev/null @@ -1,8 +0,0 @@ -package namespace.exceptions - -import data.namespaces - -exception[ns] { - ns := data.namespaces[_] - startswith(ns, "builtin") -} \ No newline at end of file diff --git a/integration/testdata/fixtures/repo/rule-exception/Dockerfile b/integration/testdata/fixtures/repo/rule-exception/Dockerfile deleted file mode 100644 index 5bcf55456482..000000000000 --- a/integration/testdata/fixtures/repo/rule-exception/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM alpine:3.13 -LABEL user.root="allow" - -HEALTHCHECK NONE \ No newline at end of file diff --git a/integration/testdata/fixtures/repo/rule-exception/policy/exception.rego b/integration/testdata/fixtures/repo/rule-exception/policy/exception.rego deleted file mode 100644 index 224f626021e4..000000000000 --- a/integration/testdata/fixtures/repo/rule-exception/policy/exception.rego +++ /dev/null @@ -1,15 +0,0 @@ -package builtin.dockerfile.DS002 - -exception[rules] { - instruction := input.stages[_][_] - instruction.Cmd == "label" - - key := instruction.Value[i] - i % 2 == 0 - key == "user.root" - - value := instruction.Value[plus(i, 1)] - value == "\"allow\"" - - rules = [""] -} diff --git a/integration/testdata/fixtures/vex/file/openvex.json b/integration/testdata/fixtures/vex/file/openvex.json index 38773ba57694..9782e1716387 100644 --- a/integration/testdata/fixtures/vex/file/openvex.json +++ b/integration/testdata/fixtures/vex/file/openvex.json @@ -11,7 +11,7 @@ { "@id": "pkg:golang/github.com/testdata/testdata", "subcomponents": [ - { "@id": "pkg:golang/github.com/open-policy-agent/opa@0.35.0" } + { "@id": "pkg:golang/github.com/open-policy-agent/opa@v0.35.0" } ] } ], diff --git a/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden b/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden index 65fc78e6c66f..9f23585a01da 100644 --- a/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden +++ b/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden @@ -6169,6 +6169,12 @@ "type": "library", "name": "activesupport", "version": "6.0.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "a2cd09dcbaf8ca1951fb8e3f2ebdfe6728ab44f7" + } + ], "licenses": [ { "license": { @@ -6201,6 +6207,12 @@ "type": "library", "name": "addressable", "version": "2.7.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "b4596fdeffcb1c89b24623b6f775a6b054a8323f" + } + ], "licenses": [ { "license": { @@ -6233,6 +6245,12 @@ "type": "library", "name": "concurrent-ruby", "version": "1.1.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "c96749b0390ad63300b13dca6fd83e5508facf18" + } + ], "licenses": [ { "license": { @@ -6265,6 +6283,12 @@ "type": "library", "name": "cool.io", "version": "1.6.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "706a2490b54301e8ae8f2ca8f9f56b279b96ac7b" + } + ], "purl": "pkg:gem/cool.io@1.6.0", "properties": [ { @@ -6290,6 +6314,12 @@ "type": "library", "name": "dig_rb", "version": "1.0.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "04a4a555fe3a7e253098e870cf8a6c8746828829" + } + ], "licenses": [ { "license": { @@ -6322,6 +6352,12 @@ "type": "library", "name": "domain_name", "version": "0.5.20190701", + "hashes": [ + { + "alg": "SHA-1", + "content": "e45a352deedbf1d48c2563caa583d0864d6ac62b" + } + ], "licenses": [ { "license": { @@ -6364,6 +6400,12 @@ "type": "library", "name": "elasticsearch-api", "version": "7.5.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "aac794d1d845525dc57d73d8bd5bda4b7f593ea4" + } + ], "licenses": [ { "license": { @@ -6396,6 +6438,12 @@ "type": "library", "name": "elasticsearch-transport", "version": "7.5.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "29ab0a306cfc109b82ac19c37f288956a4d6d1d9" + } + ], "licenses": [ { "license": { @@ -6428,6 +6476,12 @@ "type": "library", "name": "elasticsearch", "version": "7.5.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "f3996e145e83f80d27ed48f8d2dca84f02c696c3" + } + ], "licenses": [ { "license": { @@ -6460,6 +6514,12 @@ "type": "library", "name": "excon", "version": "0.72.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "8b5c81a189d2748ae488dff8a7b4876493b86f76" + } + ], "licenses": [ { "license": { @@ -6492,6 +6552,12 @@ "type": "library", "name": "faraday", "version": "0.17.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "b8c741fbdc2d729a59e2e855037421040673ca45" + } + ], "licenses": [ { "license": { @@ -6524,6 +6590,12 @@ "type": "library", "name": "ffi-compiler", "version": "1.0.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "b9ffee214ef79e695c14c8703566f7c13be4c2ba" + } + ], "licenses": [ { "license": { @@ -6556,6 +6628,12 @@ "type": "library", "name": "ffi", "version": "1.12.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "e6345da46b7a923b2248bc76d074362e7491376b" + } + ], "licenses": [ { "license": { @@ -6588,6 +6666,12 @@ "type": "library", "name": "fluent-plugin-concat", "version": "2.4.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "b6a0da88821e50d052cb244b57562f00abe79888" + } + ], "licenses": [ { "license": { @@ -6620,6 +6704,12 @@ "type": "library", "name": "fluent-plugin-detect-exceptions", "version": "0.0.13", + "hashes": [ + { + "alg": "SHA-1", + "content": "d1a7b50f7723ead908453f463e24424735be0a56" + } + ], "licenses": [ { "license": { @@ -6652,6 +6742,12 @@ "type": "library", "name": "fluent-plugin-elasticsearch", "version": "3.8.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "d60372f3af2757abd0a4ff68484e9328b2cbe386" + } + ], "licenses": [ { "license": { @@ -6684,6 +6780,12 @@ "type": "library", "name": "fluent-plugin-kubernetes_metadata_filter", "version": "2.4.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "eeb3ce046c69c9c83ed1d4bc949058ef6a124f96" + } + ], "licenses": [ { "license": { @@ -6716,6 +6818,12 @@ "type": "library", "name": "fluent-plugin-multi-format-parser", "version": "1.0.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "9aa245fc07627474621e29f04507a377dfae09d3" + } + ], "licenses": [ { "license": { @@ -6748,6 +6856,12 @@ "type": "library", "name": "fluent-plugin-prometheus", "version": "1.7.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "597a311791f0d05968c558e8015c8bed864137e2" + } + ], "licenses": [ { "license": { @@ -6780,6 +6894,12 @@ "type": "library", "name": "fluent-plugin-systemd", "version": "1.0.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "115901208913bc5031597a20fae33c50c6de6500" + } + ], "licenses": [ { "license": { @@ -6812,6 +6932,12 @@ "type": "library", "name": "fluentd", "version": "1.8.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "5f31ca316e345410e5a5b70b5fafed8a51fd5092" + } + ], "licenses": [ { "license": { @@ -6844,6 +6970,12 @@ "type": "library", "name": "http-accept", "version": "1.7.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "21dedf2ba79a24f86528c2dfc32d17dd9324d9fd" + } + ], "purl": "pkg:gem/http-accept@1.7.0", "properties": [ { @@ -6869,6 +7001,12 @@ "type": "library", "name": "http-cookie", "version": "1.0.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "e6f5b8e237e694b3729797cca134525822769964" + } + ], "licenses": [ { "license": { @@ -6901,6 +7039,12 @@ "type": "library", "name": "http-form_data", "version": "2.2.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "53c844c1f954a9c43b78b8e57f18c0ec965beb1f" + } + ], "licenses": [ { "license": { @@ -6933,6 +7077,12 @@ "type": "library", "name": "http-parser", "version": "1.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "7c6889d98755a1fe8859d850892412a0e001ba9a" + } + ], "licenses": [ { "license": { @@ -6965,6 +7115,12 @@ "type": "library", "name": "http", "version": "4.3.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "79032e0328aa1d3ee184a38c50cd93d5bce8998b" + } + ], "licenses": [ { "license": { @@ -6997,6 +7153,12 @@ "type": "library", "name": "http_parser.rb", "version": "0.6.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "0ed80e936eaf7151f540186333c3df436afd46c6" + } + ], "licenses": [ { "license": { @@ -7029,6 +7191,12 @@ "type": "library", "name": "i18n", "version": "1.8.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "d13bccc2521cef33fc4303888b24f327a7369877" + } + ], "licenses": [ { "license": { @@ -7061,6 +7229,12 @@ "type": "library", "name": "kubeclient", "version": "4.6.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "31916cb42ac6b30c68a5422414946d0328be94d4" + } + ], "licenses": [ { "license": { @@ -7093,6 +7267,12 @@ "type": "library", "name": "lru_redux", "version": "1.1.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "c8c5874f406a8fefc655fee48b75dfa276a5b8fa" + } + ], "licenses": [ { "license": { @@ -7125,6 +7305,12 @@ "type": "library", "name": "mime-types-data", "version": "3.2019.1009", + "hashes": [ + { + "alg": "SHA-1", + "content": "8a80ce9bf4961df0184d25699612d29293a05aee" + } + ], "licenses": [ { "license": { @@ -7157,6 +7343,12 @@ "type": "library", "name": "mime-types", "version": "3.3.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "b70aa1555acff548ee282c76ddd562e831483187" + } + ], "licenses": [ { "license": { @@ -7189,6 +7381,12 @@ "type": "library", "name": "minitest", "version": "5.14.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "8fcf25c201eacdf1a0e4db78efcb37ad590f33c0" + } + ], "licenses": [ { "license": { @@ -7221,6 +7419,12 @@ "type": "library", "name": "msgpack", "version": "1.3.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "bda4ff6f3cd395534ba441ccacc4640f9dc43942" + } + ], "licenses": [ { "license": { @@ -7253,6 +7457,12 @@ "type": "library", "name": "multi_json", "version": "1.14.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "fead333877a2db2e2aaca87d8cd1f270952cd42e" + } + ], "licenses": [ { "license": { @@ -7285,6 +7495,12 @@ "type": "library", "name": "multipart-post", "version": "2.1.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "927edb51d5b23a49a417fe1503f196896c0e8034" + } + ], "licenses": [ { "license": { @@ -7317,6 +7533,12 @@ "type": "library", "name": "netrc", "version": "0.11.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "98d7bbb894429413feb5c0a3b766a7945f65e3ba" + } + ], "licenses": [ { "license": { @@ -7349,6 +7571,12 @@ "type": "library", "name": "oj", "version": "3.10.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "b348b933b9c2f6a6e952f6a15c7cbd9f1186815f" + } + ], "licenses": [ { "license": { @@ -7381,6 +7609,12 @@ "type": "library", "name": "prometheus-client", "version": "0.9.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "f829d25ee6b39cdda518f7b6f85be6563d7b5990" + } + ], "licenses": [ { "license": { @@ -7413,6 +7647,12 @@ "type": "library", "name": "public_suffix", "version": "4.0.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "ac88907845a8bec2a0df25cf2e3ef61121e47252" + } + ], "licenses": [ { "license": { @@ -7445,6 +7685,12 @@ "type": "library", "name": "quantile", "version": "0.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "07a0817cd9df688930b2d8481a647a7ec321b870" + } + ], "licenses": [ { "license": { @@ -7477,6 +7723,12 @@ "type": "library", "name": "rake", "version": "13.0.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "d01a832a472daf914670adda88b44b419a4d2daf" + } + ], "licenses": [ { "license": { @@ -7509,6 +7761,12 @@ "type": "library", "name": "recursive-open-struct", "version": "1.1.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "5a4a02765d82d6786a832384b890f0a2497c2e14" + } + ], "licenses": [ { "license": { @@ -7541,6 +7799,12 @@ "type": "library", "name": "rest-client", "version": "2.1.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "a7e5943a216b16e0867693f20d5d1604cd015486" + } + ], "licenses": [ { "license": { @@ -7573,6 +7837,12 @@ "type": "library", "name": "serverengine", "version": "2.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "16e5806b2d513f6b075355c602aed0f960584267" + } + ], "licenses": [ { "license": { @@ -7605,6 +7875,12 @@ "type": "library", "name": "sigdump", "version": "0.2.4", + "hashes": [ + { + "alg": "SHA-1", + "content": "1bae5c6042dc82a4bec6aacb42f75ba71f7cb634" + } + ], "licenses": [ { "license": { @@ -7637,6 +7913,12 @@ "type": "library", "name": "strptime", "version": "0.2.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "17150d9e40754ea1a732796f224b9be78e77b86a" + } + ], "licenses": [ { "license": { @@ -7669,6 +7951,12 @@ "type": "library", "name": "systemd-journal", "version": "1.3.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "4f310622fe58e95897147736c96d3d42174a3363" + } + ], "licenses": [ { "license": { @@ -7701,6 +7989,12 @@ "type": "library", "name": "thread_safe", "version": "0.3.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "546993ac33864e279ae73e918d6da5d4ca083098" + } + ], "licenses": [ { "license": { @@ -7733,6 +8027,12 @@ "type": "library", "name": "tzinfo-data", "version": "1.2019.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "26832d11382943b02433f3ad1df7653b4cfdf3a2" + } + ], "licenses": [ { "license": { @@ -7765,6 +8065,12 @@ "type": "library", "name": "tzinfo", "version": "1.2.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "5b7db490d431d97366729086683e736d2b5fee99" + } + ], "licenses": [ { "license": { @@ -7797,6 +8103,12 @@ "type": "library", "name": "unf", "version": "0.1.4", + "hashes": [ + { + "alg": "SHA-1", + "content": "85ff87b60a6d16ffddf4db5f5f91c0ef76bacd3d" + } + ], "licenses": [ { "license": { @@ -7829,6 +8141,12 @@ "type": "library", "name": "unf_ext", "version": "0.0.7.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "1b5141ee855f16e832534c2e73d81fec0601ebd3" + } + ], "licenses": [ { "license": { @@ -7861,6 +8179,12 @@ "type": "library", "name": "yajl-ruby", "version": "1.4.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "670f3cd2fc601c9b7fde02b1d8c60e90491a7221" + } + ], "licenses": [ { "license": { @@ -7893,6 +8217,12 @@ "type": "library", "name": "zeitwerk", "version": "2.3.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "803894c06d28932016866a26fc2b22c4db942094" + } + ], "licenses": [ { "license": { diff --git a/integration/testdata/gomod-skip.json.golden b/integration/testdata/gomod-skip.json.golden index 69ab998ec9dd..877fd627b89a 100644 --- a/integration/testdata/gomod-skip.json.golden +++ b/integration/testdata/gomod-skip.json.golden @@ -26,10 +26,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "de19cd663ca047a8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "9d949a7b01249e68" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -53,10 +53,10 @@ "PkgID": "github.com/open-policy-agent/opa@v0.35.0", "PkgName": "github.com/open-policy-agent/opa", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/open-policy-agent/opa@0.35.0", - "UID": "6b685002e082ffc5" + "PURL": "pkg:golang/github.com/open-policy-agent/opa@v0.35.0", + "UID": "e89e2b0d8977e2a" }, - "InstalledVersion": "0.35.0", + "InstalledVersion": "v0.35.0", "FixedVersion": "0.37.0", "Status": "fixed", "Layer": {}, @@ -100,10 +100,10 @@ "PkgID": "golang.org/x/text@v0.3.6", "PkgName": "golang.org/x/text", "PkgIdentifier": { - "PURL": "pkg:golang/golang.org/x/text@0.3.6", - "UID": "825dc613c0f39d45" + "PURL": "pkg:golang/golang.org/x/text@v0.3.6", + "UID": "3050088ce9eb2ce4" }, - "InstalledVersion": "0.3.6", + "InstalledVersion": "v0.3.6", "FixedVersion": "0.3.7", "Status": "fixed", "Layer": {}, @@ -133,10 +133,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94376dc37054a7e8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "2f7f0fa81860b8f1" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, diff --git a/integration/testdata/gomod-vex.json.golden b/integration/testdata/gomod-vex.json.golden index a2269bd1d0b7..34d96dc0ce95 100644 --- a/integration/testdata/gomod-vex.json.golden +++ b/integration/testdata/gomod-vex.json.golden @@ -26,10 +26,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "de19cd663ca047a8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "9d949a7b01249e68" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -53,10 +53,10 @@ "PkgID": "golang.org/x/text@v0.3.6", "PkgName": "golang.org/x/text", "PkgIdentifier": { - "PURL": "pkg:golang/golang.org/x/text@0.3.6", - "UID": "825dc613c0f39d45" + "PURL": "pkg:golang/golang.org/x/text@v0.3.6", + "UID": "3050088ce9eb2ce4" }, - "InstalledVersion": "0.3.6", + "InstalledVersion": "v0.3.6", "FixedVersion": "0.3.7", "Status": "fixed", "Layer": {}, @@ -86,10 +86,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94376dc37054a7e8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "2f7f0fa81860b8f1" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -120,10 +120,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94306cdcf85fb50a" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "3ad40723ed2fce22" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, diff --git a/integration/testdata/gomod.json.golden b/integration/testdata/gomod.json.golden index 627088188285..551b787cadd6 100644 --- a/integration/testdata/gomod.json.golden +++ b/integration/testdata/gomod.json.golden @@ -26,10 +26,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "de19cd663ca047a8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "9d949a7b01249e68" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -53,10 +53,10 @@ "PkgID": "github.com/open-policy-agent/opa@v0.35.0", "PkgName": "github.com/open-policy-agent/opa", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/open-policy-agent/opa@0.35.0", - "UID": "6b685002e082ffc5" + "PURL": "pkg:golang/github.com/open-policy-agent/opa@v0.35.0", + "UID": "e89e2b0d8977e2a" }, - "InstalledVersion": "0.35.0", + "InstalledVersion": "v0.35.0", "FixedVersion": "0.37.0", "Status": "fixed", "Layer": {}, @@ -100,10 +100,10 @@ "PkgID": "golang.org/x/text@v0.3.6", "PkgName": "golang.org/x/text", "PkgIdentifier": { - "PURL": "pkg:golang/golang.org/x/text@0.3.6", - "UID": "825dc613c0f39d45" + "PURL": "pkg:golang/golang.org/x/text@v0.3.6", + "UID": "3050088ce9eb2ce4" }, - "InstalledVersion": "0.3.6", + "InstalledVersion": "v0.3.6", "FixedVersion": "0.3.7", "Status": "fixed", "Layer": {}, @@ -133,10 +133,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94376dc37054a7e8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "2f7f0fa81860b8f1" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -167,10 +167,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94306cdcf85fb50a" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "3ad40723ed2fce22" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, diff --git a/integration/testdata/helm.json.golden b/integration/testdata/helm.json.golden index 518458ac1088..36ac8cf19935 100644 --- a/integration/testdata/helm.json.golden +++ b/integration/testdata/helm.json.golden @@ -21,9 +21,8 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 80, - "Failures": 14, - "Exceptions": 0 + "Successes": 79, + "Failures": 14 }, "Misconfigurations": [ { @@ -890,10 +889,11 @@ "Namespace": "builtin.kubernetes.KSV117", "Query": "data.builtin.kubernetes.KSV117.deny", "Resolution": "Do not map the container ports to privileged host ports when starting a container.", - "Severity": "HIGH", + "Severity": "MEDIUM", "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv117", "References": [ "https://kubernetes.io/docs/concepts/security/pod-security-standards/", + "https://www.stigviewer.com/stig/kubernetes/2022-12-02/finding/V-242414", "https://avd.aquasec.com/misconfig/ksv117" ], "Status": "FAIL", diff --git a/integration/testdata/helm_testchart.json.golden b/integration/testdata/helm_testchart.json.golden index 2e659b13e68a..478bcea3b672 100644 --- a/integration/testdata/helm_testchart.json.golden +++ b/integration/testdata/helm_testchart.json.golden @@ -21,9 +21,8 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 90, - "Failures": 4, - "Exceptions": 0 + "Successes": 89, + "Failures": 4 }, "Misconfigurations": [ { @@ -318,10 +317,11 @@ "Namespace": "builtin.kubernetes.KSV117", "Query": "data.builtin.kubernetes.KSV117.deny", "Resolution": "Do not map the container ports to privileged host ports when starting a container.", - "Severity": "HIGH", + "Severity": "MEDIUM", "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv117", "References": [ "https://kubernetes.io/docs/concepts/security/pod-security-standards/", + "https://www.stigviewer.com/stig/kubernetes/2022-12-02/finding/V-242414", "https://avd.aquasec.com/misconfig/ksv117" ], "Status": "FAIL", @@ -341,9 +341,8 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 61, - "Failures": 0, - "Exceptions": 0 + "Successes": 60, + "Failures": 0 } }, { @@ -351,9 +350,8 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 60, - "Failures": 0, - "Exceptions": 0 + "Successes": 59, + "Failures": 0 } } ] diff --git a/integration/testdata/helm_testchart.overridden.json.golden b/integration/testdata/helm_testchart.overridden.json.golden index f4c984daa458..36710b35734e 100644 --- a/integration/testdata/helm_testchart.overridden.json.golden +++ b/integration/testdata/helm_testchart.overridden.json.golden @@ -21,9 +21,8 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 88, - "Failures": 6, - "Exceptions": 0 + "Successes": 87, + "Failures": 6 }, "Misconfigurations": [ { @@ -545,10 +544,11 @@ "Namespace": "builtin.kubernetes.KSV117", "Query": "data.builtin.kubernetes.KSV117.deny", "Resolution": "Do not map the container ports to privileged host ports when starting a container.", - "Severity": "HIGH", + "Severity": "MEDIUM", "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv117", "References": [ "https://kubernetes.io/docs/concepts/security/pod-security-standards/", + "https://www.stigviewer.com/stig/kubernetes/2022-12-02/finding/V-242414", "https://avd.aquasec.com/misconfig/ksv117" ], "Status": "FAIL", @@ -568,9 +568,8 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 61, - "Failures": 0, - "Exceptions": 0 + "Successes": 60, + "Failures": 0 } }, { @@ -578,9 +577,8 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 60, - "Failures": 0, - "Exceptions": 0 + "Successes": 59, + "Failures": 0 } } ] diff --git a/integration/testdata/julia-spdx.json.golden b/integration/testdata/julia-spdx.json.golden index 89a4edb3f287..a770765dd849 100644 --- a/integration/testdata/julia-spdx.json.golden +++ b/integration/testdata/julia-spdx.json.golden @@ -17,11 +17,21 @@ "SPDXID": "SPDXRef-Application-18fc3597717a3e56", "downloadLocation": "NONE", "filesAnalyzed": false, - "attributionTexts": [ - "Class: lang-pkgs", - "Type: julia" - ], - "primaryPackagePurpose": "APPLICATION" + "primaryPackagePurpose": "APPLICATION", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "Class: lang-pkgs" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "Type: julia" + } + ] }, { "name": "A", @@ -40,11 +50,21 @@ "referenceLocator": "pkg:julia/A@1.9.0?uuid=ead4f63c-334e-11e9-00e6-e7f0a5f21b60" } ], - "attributionTexts": [ - "PkgID: ead4f63c-334e-11e9-00e6-e7f0a5f21b60", - "PkgType: julia" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgID: ead4f63c-334e-11e9-00e6-e7f0a5f21b60" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: julia" + } + ] }, { "name": "B", @@ -63,11 +83,21 @@ "referenceLocator": "pkg:julia/B@1.9.0?uuid=f41f7b98-334e-11e9-1257-49272045fb24" } ], - "attributionTexts": [ - "PkgID: f41f7b98-334e-11e9-1257-49272045fb24", - "PkgType: julia" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgID: f41f7b98-334e-11e9-1257-49272045fb24" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: julia" + } + ] }, { "name": "B", @@ -86,21 +116,36 @@ "referenceLocator": "pkg:julia/B@1.9.0?uuid=edca9bc6-334e-11e9-3554-9595dbb4349c" } ], - "attributionTexts": [ - "PkgID: edca9bc6-334e-11e9-3554-9595dbb4349c", - "PkgType: julia" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgID: edca9bc6-334e-11e9-3554-9595dbb4349c" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: julia" + } + ] }, { "name": "testdata/fixtures/repo/julia", "SPDXID": "SPDXRef-Filesystem-1be792dd0077c431", "downloadLocation": "NONE", "filesAnalyzed": false, - "attributionTexts": [ - "SchemaVersion: 2" - ], - "primaryPackagePurpose": "SOURCE" + "primaryPackagePurpose": "SOURCE", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "SchemaVersion: 2" + } + ] } ], "relationships": [ diff --git a/integration/testdata/npm.gitlab.golden b/integration/testdata/npm.gitlab.golden new file mode 100644 index 000000000000..6c8cc2ce97ba --- /dev/null +++ b/integration/testdata/npm.gitlab.golden @@ -0,0 +1,214 @@ +{ + "version": "15.0.7", + "scan": { + "analyzer": { + "id": "trivy", + "name": "Trivy", + "vendor": { + "name": "Aqua Security" + }, + "version": "dev" + }, + "end_time": "2021-08-25T12:20:30", + "scanner": { + "id": "trivy", + "name": "Trivy", + "url": "https://github.com/aquasecurity/trivy/", + "vendor": { + "name": "Aqua Security" + }, + "version": "dev" + }, + "start_time": "2021-08-25T12:20:30", + "status": "success", + "type": "container_scanning" + }, + "vulnerabilities": [ + { + "id": "CVE-2019-11358", + "name": "jquery: Prototype pollution in object's prototype leading to denial of service, remote code execution, or property injection", + "description": "jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution. If an unsanitized source object contained an enumerable __proto__ property, it could extend the native Object.prototype.", + "severity": "Medium", + "solution": "Upgrade jquery to 3.4.0", + "location": { + "dependency": { + "package": { + "name": "jquery" + }, + "version": "3.3.9" + }, + "operating_system": "Unknown", + "image": "Unknown" + }, + "identifiers": [ + { + "type": "cve", + "name": "CVE-2019-11358", + "value": "CVE-2019-11358", + "url": "https://avd.aquasec.com/nvd/cve-2019-11358" + } + ], + "links": [{ + "url": "http://lists.opensuse.org/opensuse-security-announce/2019-08/msg00006.html" + },{ + "url": "http://lists.opensuse.org/opensuse-security-announce/2019-08/msg00025.html" + },{ + "url": "http://packetstormsecurity.com/files/152787/dotCMS-5.1.1-Vulnerable-Dependencies.html" + },{ + "url": "http://packetstormsecurity.com/files/153237/RetireJS-CORS-Issue-Script-Execution.html" + },{ + "url": "http://packetstormsecurity.com/files/156743/OctoberCMS-Insecure-Dependencies.html" + },{ + "url": "http://seclists.org/fulldisclosure/2019/May/10" + },{ + "url": "http://seclists.org/fulldisclosure/2019/May/11" + },{ + "url": "http://seclists.org/fulldisclosure/2019/May/13" + },{ + "url": "http://www.openwall.com/lists/oss-security/2019/06/03/2" + },{ + "url": "http://www.securityfocus.com/bid/108023" + },{ + "url": "https://access.redhat.com/errata/RHBA-2019:1570" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:1456" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:2587" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:3023" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:3024" + },{ + "url": "https://access.redhat.com/security/cve/CVE-2019-11358" + },{ + "url": "https://backdropcms.org/security/backdrop-sa-core-2019-009" + },{ + "url": "https://blog.jquery.com/2019/04/10/jquery-3-4-0-released/" + },{ + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11358" + },{ + "url": "https://github.com/DanielRuf/snyk-js-jquery-174006?files=1" + },{ + "url": "https://github.com/advisories/GHSA-6c3j-c64m-qhgq" + },{ + "url": "https://github.com/jquery/jquery/commit/753d591aea698e57d6db58c9f722cd0808619b1b" + },{ + "url": "https://github.com/jquery/jquery/pull/4333" + },{ + "url": "https://github.com/rails/jquery-rails/blob/master/CHANGELOG.md#434" + },{ + "url": "https://hackerone.com/reports/454365" + },{ + "url": "https://kb.pulsesecure.net/articles/Pulse_Security_Advisories/SA44601" + },{ + "url": "https://linux.oracle.com/cve/CVE-2019-11358.html" + },{ + "url": "https://linux.oracle.com/errata/ELSA-2020-4847.html" + },{ + "url": "https://lists.apache.org/thread.html/08720ef215ee7ab3386c05a1a90a7d1c852bf0706f176a7816bf65fc@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/519eb0fd45642dcecd9ff74cb3e71c20a4753f7d82e2f07864b5108f@%3Cdev.drill.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/5928aa293e39d248266472210c50f176cac1535220f2486e6a7fa844@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/6097cdbd6f0a337bedd9bb5cc441b2d525ff002a96531de367e4259f@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/88fb0362fd40e5b605ea8149f63241537b8b6fb5bfa315391fc5cbb7@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/b0656d359c7d40ec9f39c8cc61bca66802ef9a2a12ee199f5b0c1442@%3Cdev.drill.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/b736d0784cf02f5a30fbb4c5902762a15ad6d47e17e2c5a17b7d6205@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/ba79cf1658741e9f146e4c59b50aee56656ea95d841d358d006c18b6@%3Ccommits.roller.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/bcce5a9c532b386c68dab2f6b3ce8b0cc9b950ec551766e76391caa3@%3Ccommits.nifi.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/f9bc3e55f4e28d1dcd1a69aae6d53e609a758e34d2869b4d798e13cc@%3Cissues.drill.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r2041a75d3fc09dec55adfd95d598b38d22715303f65c997c054844c9@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r2baacab6e0acb5a2092eb46ae04fd6c3e8277b4fd79b1ffb7f3254fa@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r38f0d1aa3c923c22977fe7376508f030f22e22c1379fbb155bf29766@%3Cdev.syncope.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r41b5bfe009c845f67d4f68948cc9419ac2d62e287804aafd72892b08@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r7aac081cbddb6baa24b75e74abf0929bf309b176755a53e3ed810355@%3Cdev.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r7d64895cc4dff84d0becfc572b20c0e4bf9bfa7b10c6f5f73e783734@%3Cdev.storm.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r7e8ebccb7c022e41295f6fdb7b971209b83702339f872ddd8cf8bf73@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/rac25da84ecdcd36f6de5ad0d255f4e967209bbbebddb285e231da37d@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/rca37935d661f4689cb4119f1b3b224413b22be161b678e6e6ce0c69b@%3Ccommits.nifi.apache.org%3E" + },{ + "url": "https://lists.debian.org/debian-lts-announce/2019/05/msg00006.html" + },{ + "url": "https://lists.debian.org/debian-lts-announce/2019/05/msg00029.html" + },{ + "url": "https://lists.debian.org/debian-lts-announce/2020/02/msg00024.html" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/4UOAZIFCSZ3ENEFOR5IXX6NFAD3HV7FA/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5IABSKTYZ5JUGL735UKGXL5YPRYOPUYI/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KYH3OAGR2RTCHRA5NOKX2TES7SNQMWGO/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/QV3PKZC3PQCO3273HAT76PAQZFBEO4KP/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RLXRX23725JL366CNZGJZ7AQQB7LHQ6F/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/WZW27UCJ5CYFL4KFFFMYMIBNMIU2ALG5/" + },{ + "url": "https://nvd.nist.gov/vuln/detail/CVE-2019-11358" + },{ + "url": "https://seclists.org/bugtraq/2019/Apr/32" + },{ + "url": "https://seclists.org/bugtraq/2019/Jun/12" + },{ + "url": "https://seclists.org/bugtraq/2019/May/18" + },{ + "url": "https://security.netapp.com/advisory/ntap-20190919-0001/" + },{ + "url": "https://snyk.io/vuln/SNYK-JS-JQUERY-174006" + },{ + "url": "https://www.debian.org/security/2019/dsa-4434" + },{ + "url": "https://www.debian.org/security/2019/dsa-4460" + },{ + "url": "https://www.drupal.org/sa-core-2019-006" + },{ + "url": "https://www.oracle.com//security-alerts/cpujul2021.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuApr2021.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuapr2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpujan2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpujan2021.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpujul2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuoct2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuoct2021.html" + },{ + "url": "https://www.oracle.com/technetwork/security-advisory/cpujul2019-5072835.html" + },{ + "url": "https://www.oracle.com/technetwork/security-advisory/cpuoct2019-5072832.html" + },{ + "url": "https://www.privacy-wise.com/mitigating-cve-2019-11358-in-old-versions-of-jquery/" + },{ + "url": "https://www.synology.com/security/advisory/Synology_SA_19_19" + },{ + "url": "https://www.tenable.com/security/tns-2019-08" + },{ + "url": "https://www.tenable.com/security/tns-2020-02" + } + ] + } + ], + "remediations": [] +} diff --git a/integration/testimages.ini b/integration/testimages.ini new file mode 100644 index 000000000000..778fbc9b8e63 --- /dev/null +++ b/integration/testimages.ini @@ -0,0 +1,3 @@ +# Configuration file for both shell scripts and Go programs +TEST_IMAGES=ghcr.io/knqyf263/trivy-test-images +TEST_VM_IMAGES=ghcr.io/knqyf263/trivy-test-vm-images diff --git a/internal/testutil/image.go b/internal/testutil/image.go new file mode 100644 index 000000000000..dba54bc23dff --- /dev/null +++ b/internal/testutil/image.go @@ -0,0 +1,67 @@ +package testutil + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +var ( + testImages string + testVMImages string +) + +func init() { + _, b, _, _ := runtime.Caller(0) + currentDir := filepath.Dir(b) + f, err := os.Open(filepath.Join(currentDir, "..", "..", "integration", "testimages.ini")) + if err != nil { + panic(err) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), "#") { + continue + } + parts := strings.SplitN(scanner.Text(), "=", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + switch key { + case "TEST_IMAGES": + testImages = value + case "TEST_VM_IMAGES": + testVMImages = value + } + } + } + if err = scanner.Err(); err != nil { + panic(err) + } +} + +func ImageName(subpath, tag, digest string) string { + return imageName(testImages, subpath, tag, digest) +} + +func VMImageName(subpath, tag, digest string) string { + return imageName(testVMImages, subpath, tag, digest) +} + +func imageName(img, subpath, tag, digest string) string { + if subpath != "" { + img = fmt.Sprintf("%s/%s", img, subpath) + } + if tag != "" { + img = fmt.Sprintf("%s:%s", img, tag) + } + if digest != "" { + img = fmt.Sprintf("%s@%s", img, digest) + } + return img +} diff --git a/magefiles/fixture.go b/magefiles/fixture.go index 0ed9ae8d4217..0112a8421133 100644 --- a/magefiles/fixture.go +++ b/magefiles/fixture.go @@ -10,13 +10,13 @@ import ( "github.com/google/go-containerregistry/pkg/crane" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/magefile/mage/sh" + + "github.com/aquasecurity/trivy/internal/testutil" ) func fixtureContainerImages() error { - const ( - testImages = "ghcr.io/aquasecurity/trivy-test-images" - dir = "integration/testdata/fixtures/images/" - ) + var testImages = testutil.ImageName("", "", "") + const dir = "integration/testdata/fixtures/images/" if err := os.MkdirAll(dir, 0750); err != nil { return err } @@ -48,8 +48,8 @@ func fixtureContainerImages() error { } func fixtureVMImages() error { + var testVMImages = testutil.VMImageName("", "", "") const ( - testVMImages = "ghcr.io/aquasecurity/trivy-test-vm-images" titleAnnotation = "org.opencontainers.image.title" dir = "integration/testdata/fixtures/vm-images/" ) diff --git a/magefiles/helm.go b/magefiles/helm.go new file mode 100644 index 000000000000..443c57eed029 --- /dev/null +++ b/magefiles/helm.go @@ -0,0 +1,117 @@ +//go:build mage_helm + +package main + +import ( + "fmt" + "log" + "os" + + "github.com/aquasecurity/go-version/pkg/semver" + + "github.com/magefile/mage/sh" + "golang.org/x/xerrors" + "gopkg.in/yaml.v3" +) + +const chartFile = "./helm/trivy/Chart.yaml" + +func main() { + trivyVersion, err := version() + if err != nil { + log.Fatalf("could not determine Trivy version: %v", err) + } + + newHelmVersion, err := bumpHelmChart(chartFile, trivyVersion) + if err != nil { + log.Fatalf("could not bump Trivy version to %q: %v", trivyVersion, err) + } + + log.Printf("Current helm version will bump up %q with Trivy %q", newHelmVersion, trivyVersion) + + newBranch := fmt.Sprintf("ci/helm-chart/bump-trivy-to-%s", trivyVersion) + title := fmt.Sprintf("ci(helm): bump Trivy version to %s for Trivy Helm Chart %s", trivyVersion, newHelmVersion) + description := fmt.Sprintf("This PR bumps Trivy up to the %s version for the Trivy Helm chart %s.", + trivyVersion, newHelmVersion) + + cmds := [][]string{ + []string{"git", "switch", "-c", newBranch}, + []string{"git", "add", chartFile}, + []string{"git", "commit", "-m", title}, + []string{"git", "push", "origin", newBranch}, + []string{"gh", "pr", "create", "--base", "main", "--head", newBranch, "--title", title, "--body", description, "--repo", "$GITHUB_REPOSITORY"}, + } + + if err := runShCommands(cmds); err != nil { + log.Fatal(err) + } + log.Print("Successfully created PR with a new helm version") +} + +type Chart struct { + Version string `yaml:"version"` + AppVersion string `yaml:"appVersion"` +} + +// bumpHelmChart bumps up helm and trivy versions inside a file (Chart.yaml) +// it returns a new helm version and error +func bumpHelmChart(filename, trivyVersion string) (string, error) { + input, err := os.ReadFile(filename) + if err != nil { + return "", xerrors.Errorf("could not read file %q: %w", filename, err) + } + currentHelmChart := &Chart{} + if err := yaml.Unmarshal(input, currentHelmChart); err != nil { + return "", xerrors.Errorf("could not unmarshal helm chart %q: %w", filename, err) + } + + newHelmVersion, err := buildNewHelmVersion(currentHelmChart.Version, currentHelmChart.AppVersion, trivyVersion) + if err != nil { + return "", xerrors.Errorf("could not build new helm version: %v", err) + } + cmds := [][]string{ + []string{"sed", "-i", "-e", fmt.Sprintf("s/appVersion: %s/appVersion: %s/g", currentHelmChart.AppVersion, trivyVersion), filename}, + []string{"sed", "-i", "-e", fmt.Sprintf("s/version: %s/version: %s/g", currentHelmChart.Version, newHelmVersion), filename}, + } + + if err := runShCommands(cmds); err != nil { + return "", xerrors.Errorf("could not update Helm Chart %q: %w", newHelmVersion, err) + } + return newHelmVersion, nil +} + +func runShCommands(cmds [][]string) error { + for _, cmd := range cmds { + if err := sh.Run(cmd[0], cmd[1:]...); err != nil { + return xerrors.Errorf("failed to run %v: %w", cmd, err) + } + } + return nil +} + +func buildNewHelmVersion(currentHelm, currentTrivy, newTrivy string) (string, error) { + currentHelmVersion, err := semver.Parse(currentHelm) + if err != nil { + return "", xerrors.Errorf("could not parse current helm version: %w", err) + } + + currentTrivyVersion, err := semver.Parse(currentTrivy) + if err != nil { + return "", xerrors.Errorf("could not parse current trivy version: %w", err) + } + + newTrivyVersion, err := semver.Parse(newTrivy) + if err != nil { + return "", xerrors.Errorf("could not parse new trivy version: %w", err) + } + + if newTrivyVersion.Major().Compare(currentTrivyVersion.Major()) > 0 { + return currentHelmVersion.IncMajor().String(), nil + } + + if newTrivyVersion.Minor().Compare(currentTrivyVersion.Minor()) > 0 { + return currentHelmVersion.IncMinor().String(), nil + } + + return currentHelmVersion.IncPatch().String(), nil +} diff --git a/magefiles/helm_test.go b/magefiles/helm_test.go new file mode 100644 index 000000000000..f2e3233d2879 --- /dev/null +++ b/magefiles/helm_test.go @@ -0,0 +1,92 @@ +//go:build mage_helm + +package main + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewVersion(t *testing.T) { + tests := []struct { + name string + currentHelmVersion string + currentTrivyVersion string + newTrivyVersion string + newHelmVersion string + }{ + { + "created the first patch", + "0.1.0", + "0.55.0", + "0.55.1", + "0.1.1", + }, + { + "created the second patch", + "0.1.1", + "0.55.1", + "0.55.2", + "0.1.2", + }, + { + "created the second patch but helm chart was changed", + "0.1.2", + "0.55.1", + "0.55.2", + "0.1.3", + }, + { + "created a new minor version", + "0.1.1", + "0.55.1", + "0.56.0", + "0.2.0", + }, + { + "created a new major version", + "0.1.1", + "0.55.1", + "1.0.0", + "1.0.0", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + newHelmVersion, err := buildNewHelmVersion(test.currentHelmVersion, test.currentTrivyVersion, test.newTrivyVersion) + assert.NoError(t, err) + assert.Equal(t, test.newHelmVersion, newHelmVersion) + }) + } +} + +func TestBumpHelmChart_Success(t *testing.T) { + tempFile, err := os.CreateTemp(t.TempDir(), "Chart-*.yaml") + assert.NoError(t, err) + + content := ` +apiVersion: v2 +name: trivy +version: 0.8.0 +appVersion: 0.55.0 +description: Trivy helm chart +keywords: + - scanner + - trivy + - vulnerability +` + err = os.WriteFile(tempFile.Name(), []byte(content), 0644) + assert.NoError(t, err) + + newVersion, err := bumpHelmChart(tempFile.Name(), "0.55.1") + assert.NoError(t, err) + assert.Equal(t, "0.8.1", newVersion) + + updatedContent, err := os.ReadFile(tempFile.Name()) + assert.NoError(t, err) + assert.Contains(t, string(updatedContent), "appVersion: 0.55.1") + assert.Contains(t, string(updatedContent), "version: 0.8.1") +} diff --git a/magefiles/magefile.go b/magefiles/magefile.go index 06ebd7e665e1..f70491bce2e8 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -489,3 +489,10 @@ func (CloudActions) Generate() error { func VEX(_ context.Context, dir string) error { return sh.RunWith(ENV, "go", "run", "-tags=mage_vex", "./magefiles/vex.go", "--dir", dir) } + +type Helm mg.Namespace + +// UpdateVersion updates a version for Trivy Helm Chart and creates a PR +func (Helm) UpdateVersion() error { + return sh.RunWith(ENV, "go", "run", "-tags=mage_helm", "./magefiles") +} diff --git a/misc/lint/rules.go b/misc/lint/rules.go index e477175a244f..4f1b098535ec 100644 --- a/misc/lint/rules.go +++ b/misc/lint/rules.go @@ -20,3 +20,13 @@ func initializeMaps(m dsl.Matcher) { Suggest(`make(map[$key]$value)`). Report(`replace '$$' with 'make(map[$key]$value)`) } + +// While errors.Join from standard library can combine multiple errors, +// we use hashicorp/go-multierror for more user-friendly error outputs. +func errorsJoin(m dsl.Matcher) { + m.Match(`errors.Join($x...)`). + Report("use github.com/hashicorp/go-multierror.Append instead of errors.Join.") + + m.Match(`errors.Join($*args)`). + Report("use github.com/hashicorp/go-multierror.Append instead of errors.Join.") +} diff --git a/mkdocs.yml b/mkdocs.yml index abe494df9dd9..ab3d51b71275 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,7 +56,6 @@ nav: - Overview: docs/scanner/misconfiguration/index.md - Policy: - Built-in Checks: docs/scanner/misconfiguration/check/builtin.md - - Exceptions: docs/scanner/misconfiguration/check/exceptions.md - Custom Checks: - Overview: docs/scanner/misconfiguration/custom/index.md - Data: docs/scanner/misconfiguration/custom/data.md @@ -178,6 +177,10 @@ nav: - Plugin Update: docs/references/configuration/cli/trivy_plugin_update.md - Plugin Upgrade: docs/references/configuration/cli/trivy_plugin_upgrade.md - Plugin Search: docs/references/configuration/cli/trivy_plugin_search.md + - Registry: + - Registry: docs/references/configuration/cli/trivy_registry.md + - Registry Login: docs/references/configuration/cli/trivy_registry_login.md + - Registry Logout: docs/references/configuration/cli/trivy_registry_logout.md - Repository: docs/references/configuration/cli/trivy_repository.md - Rootfs: docs/references/configuration/cli/trivy_rootfs.md - SBOM: docs/references/configuration/cli/trivy_sbom.md diff --git a/pkg/attestation/attestation_test.go b/pkg/attestation/attestation_test.go index 763878f4ae41..0233ba30cf78 100644 --- a/pkg/attestation/attestation_test.go +++ b/pkg/attestation/attestation_test.go @@ -27,7 +27,7 @@ func TestStatement_UnmarshalJSON(t *testing.T) { PredicateType: "cosign.sigstore.dev/attestation/v1", Subject: []in_toto.Subject{ { - Name: "ghcr.io/aquasecurity/trivy-test-images", + Name: "ghcr.io/aquasecurity/trivy-test-images", // Defined in the attestations.json file Digest: slsa.DigestSet{ "sha256": "72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb", }, diff --git a/pkg/cache/mock_artifact_cache.go b/pkg/cache/mock_artifact_cache.go index e0b1430337e4..ef9503203a7f 100644 --- a/pkg/cache/mock_artifact_cache.go +++ b/pkg/cache/mock_artifact_cache.go @@ -229,9 +229,6 @@ func (_m *MockArtifactCache) PutBlob(blobID string, blobInfo types.BlobInfo) err for j := range blobInfo.Misconfigurations[i].Warnings { blobInfo.Misconfigurations[i].Warnings[j].Code = types.Code{} } - for j := range blobInfo.Misconfigurations[i].Exceptions { - blobInfo.Misconfigurations[i].Exceptions[j].Code = types.Code{} - } } ret := _m.Called(blobID, blobInfo) diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 23b44fdec553..dbe3d54ba373 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -15,6 +15,7 @@ import ( "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/commands/artifact" + "github.com/aquasecurity/trivy/pkg/commands/auth" "github.com/aquasecurity/trivy/pkg/commands/clean" "github.com/aquasecurity/trivy/pkg/commands/convert" "github.com/aquasecurity/trivy/pkg/commands/server" @@ -99,6 +100,7 @@ func NewApp() *cobra.Command { NewVersionCommand(globalFlags), NewVMCommand(globalFlags), NewCleanCommand(globalFlags), + NewRegistryCommand(globalFlags), NewVEXCommand(globalFlags), ) @@ -1143,7 +1145,8 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { CacheFlagGroup: flag.NewCacheFlagGroup(), DBFlagGroup: flag.NewDBFlagGroup(), PackageFlagGroup: flag.NewPackageFlagGroup(), - RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode + RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode + RegistryFlagGroup: flag.NewRegistryFlagGroup(), // for DBs in private registries ReportFlagGroup: reportFlagGroup, ScanFlagGroup: scanFlagGroup, VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(), @@ -1232,6 +1235,62 @@ func NewCleanCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { return cmd } +func NewRegistryCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { + cmd := &cobra.Command{ + Use: "registry [flags]", + GroupID: groupUtility, + Short: "Manage registry authentication", + SilenceErrors: true, + SilenceUsage: true, + } + + loginFlags := &flag.Flags{ + GlobalFlagGroup: globalFlags, + RegistryFlagGroup: flag.NewRegistryFlagGroup(), + } + loginFlags.RegistryFlagGroup.RegistryToken = nil // disable '--registry-token' + loginCmd := &cobra.Command{ + Use: "login SERVER", + Short: "Log in to a registry", + SilenceErrors: true, + SilenceUsage: true, + Example: ` # Log in to reg.example.com + cat ~/my_password.txt | trivy registry login --username foo --password-stdin reg.example.com`, + Args: cobra.ExactArgs(1), + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := loginFlags.Bind(cmd); err != nil { + return xerrors.Errorf("flag bind error: %w", err) + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + loginOpts, err := loginFlags.ToOptions(args) + if err != nil { + return xerrors.Errorf("flag error: %w", err) + } + return auth.Login(cmd.Context(), args[0], loginOpts) + }, + } + logoutCmd := &cobra.Command{ + Use: "logout SERVER", + Short: "Log out of a registry", + SilenceErrors: true, + SilenceUsage: true, + Example: ` # Log out of reg.example.com + trivy registry logout reg.example.com`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return auth.Logout(cmd.Context(), args[0]) + }, + } + loginFlags.AddFlags(loginCmd) + cmd.AddCommand(loginCmd, logoutCmd) + + cmd.SetFlagErrorFunc(flagErrorFunc) + + return cmd +} + func NewVEXCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { vexFlags := &flag.Flags{ GlobalFlagGroup: globalFlags, diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 49c7d3b34296..5018434d10c2 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -538,9 +538,9 @@ func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (Scan } } - // SPDX needs to calculate digests for package files + // SPDX and CycloneDX need to calculate digests for package files var fileChecksum bool - if opts.Format == types.FormatSPDXJSON || opts.Format == types.FormatSPDX { + if opts.Format == types.FormatSPDXJSON || opts.Format == types.FormatSPDX || opts.Format == types.FormatCycloneDX { fileChecksum = true } diff --git a/pkg/commands/auth/run.go b/pkg/commands/auth/run.go new file mode 100644 index 000000000000..7659702f2b7e --- /dev/null +++ b/pkg/commands/auth/run.go @@ -0,0 +1,109 @@ +package auth + +import ( + "context" + "net/http" + "os" + + "github.com/docker/cli/cli/config" + "github.com/docker/cli/cli/config/types" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/flag" + "github.com/aquasecurity/trivy/pkg/log" +) + +func Login(ctx context.Context, registry string, opts flag.Options) error { + if len(opts.Credentials) == 0 { + return xerrors.New("username and password required") + } else if len(opts.Credentials) > 1 { + return xerrors.New("multiple credentials are not allowed") + } + + reg, err := parseRegistry(registry, opts) + if err != nil { + return xerrors.Errorf("failed to parse registry: %w", err) + } + serverAddress := reg.Name() + + // Validate the credential + _, err = transport.NewWithContext(ctx, reg, &authn.Basic{ + Username: opts.Credentials[0].Username, + Password: opts.Credentials[0].Password, + }, httpTransport(opts), []string{reg.Scope(transport.PullScope)}) + if err != nil { + return xerrors.Errorf("failed to authenticate: %w", err) + } + + cf, err := config.Load(os.Getenv("DOCKER_CONFIG")) + if err != nil { + return xerrors.Errorf("failed to load docker config: %w", err) + } + creds := cf.GetCredentialsStore(serverAddress) + if serverAddress == name.DefaultRegistry { + serverAddress = authn.DefaultAuthKey + } + if err := creds.Store(types.AuthConfig{ + ServerAddress: serverAddress, + Username: opts.Credentials[0].Username, + Password: opts.Credentials[0].Password, + }); err != nil { + return xerrors.Errorf("failed to store credentials: %w", err) + } + + if err := cf.Save(); err != nil { + return xerrors.Errorf("failed to save docker config: %w", err) + } + log.Info("Login succeeded", log.FilePath(cf.Filename), log.String("username", opts.Credentials[0].Username)) + return nil +} + +func Logout(_ context.Context, registry string) error { + reg, err := parseRegistry(registry, flag.Options{}) + if err != nil { + return xerrors.Errorf("failed to parse registry: %w", err) + } + serverAddress := reg.Name() + + cf, err := config.Load(os.Getenv("DOCKER_CONFIG")) + if err != nil { + return xerrors.Errorf("failed to load docker config: %w", err) + } + creds := cf.GetCredentialsStore(serverAddress) + if serverAddress == name.DefaultRegistry { + serverAddress = authn.DefaultAuthKey + } + if err := creds.Erase(serverAddress); err != nil { + return xerrors.Errorf("failed to delete credentials: %w", err) + } + + if err := cf.Save(); err != nil { + return xerrors.Errorf("failed to save docker config: %w", err) + } + log.Info("Logged out", log.FilePath(cf.Filename)) + return nil +} + +func parseRegistry(registry string, opts flag.Options) (name.Registry, error) { + var nameOpts []name.Option + if opts.Insecure { + nameOpts = append(nameOpts, name.Insecure) + } + reg, err := name.NewRegistry(registry, nameOpts...) + if err != nil { + return name.Registry{}, xerrors.Errorf("failed to parse registry: %w", err) + } + return reg, nil +} + +func httpTransport(opts flag.Options) *http.Transport { + tr := remote.DefaultTransport.(*http.Transport).Clone() + if opts.Insecure { + tr.TLSClientConfig.InsecureSkipVerify = true + } + return tr +} diff --git a/pkg/commands/auth/run_test.go b/pkg/commands/auth/run_test.go new file mode 100644 index 000000000000..2b2df244031f --- /dev/null +++ b/pkg/commands/auth/run_test.go @@ -0,0 +1,142 @@ +package auth_test + +import ( + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/samber/lo" + "github.com/stretchr/testify/require" + + testauth "github.com/aquasecurity/testdocker/auth" + "github.com/aquasecurity/testdocker/registry" + "github.com/aquasecurity/trivy/pkg/commands/auth" + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/flag" +) + +func TestLogin(t *testing.T) { + type args struct { + registry string + opts flag.Options + } + tests := []struct { + name string + args args + wantErr string + }{ + { + name: "single credential", + args: args{ + opts: flag.Options{ + RegistryOptions: flag.RegistryOptions{ + Credentials: []types.Credential{ + { + Username: "user", + Password: "pass", + }, + }, + }, + }, + }, + }, + { + name: "multiple credentials", + args: args{ + opts: flag.Options{ + RegistryOptions: flag.RegistryOptions{ + Credentials: []types.Credential{ + { + Username: "user1", + Password: "pass1", + }, + { + Username: "user2", + Password: "pass2", + }, + }, + }, + }, + }, + wantErr: "multiple credentials are not allowed", + }, + { + name: "no credentials", + args: args{ + registry: "auth.test", + opts: flag.Options{}, + }, + wantErr: "username and password required", + }, + { + name: "invalid registry", + args: args{ + registry: "aaa://invalid.test", + opts: flag.Options{ + RegistryOptions: flag.RegistryOptions{ + Credentials: []types.Credential{ + { + Username: "user", + Password: "pass", + }, + }, + }, + }, + }, + wantErr: "registries must be valid RFC 3986 URI authorities", + }, + } + + tr := registry.NewDockerRegistry(registry.Option{ + Auth: testauth.Auth{ + User: "user", + Password: "pass", + }, + }) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set the DOCKER_CONFIG environment variable to a temporary directory + // so that the test does not interfere with the user's configuration. + t.Setenv("DOCKER_CONFIG", filepath.Join(t.TempDir(), "config.json")) + + reg := lo.Ternary(tt.args.registry == "", strings.TrimPrefix(tr.URL, "http://"), tt.args.registry) + err := auth.Login(context.Background(), reg, tt.args.opts) + if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + return + } + require.NoError(t, err) + }) + } +} + +func TestLogout(t *testing.T) { + // Set the DOCKER_CONFIG environment variable to a temporary directory + // so that the test does not interfere with the user's configuration. + tmpDir := t.TempDir() + t.Setenv("DOCKER_CONFIG", tmpDir) + + t.Run("success", func(t *testing.T) { + configFile := filepath.Join(tmpDir, "config.json") + err := os.WriteFile(configFile, []byte(`{"auths": {"auth.test": {"auth": "dXNlcjpwYXNz"}}}`), 0600) + require.NoError(t, err) + + err = auth.Logout(context.Background(), "auth.test") + require.NoError(t, err) + b, err := os.ReadFile(configFile) + require.NoError(t, err) + require.JSONEq(t, `{"auths": {}}`, string(b)) + }) + t.Run("not found", func(t *testing.T) { + err := auth.Logout(context.Background(), "notfound.test") + require.NoError(t, err) // Return an error if "credsStore" is "osxkeychain". + }) + + t.Run("invalid registry", func(t *testing.T) { + err := auth.Logout(context.Background(), "aaa://invalid.test") + require.ErrorContains(t, err, "registries must be valid RFC 3986 URI authorities") + }) +} diff --git a/pkg/commands/clean/run.go b/pkg/commands/clean/run.go index c60227360d19..8dd83519422f 100644 --- a/pkg/commands/clean/run.go +++ b/pkg/commands/clean/run.go @@ -2,7 +2,6 @@ package clean import ( "context" - "os" "golang.org/x/xerrors" @@ -25,7 +24,11 @@ func Run(ctx context.Context, opts flag.Options) error { } if opts.CleanAll { - return cleanAll(ctx, opts) + opts.CleanScanCache = true + opts.CleanVulnerabilityDB = true + opts.CleanJavaDB = true + opts.CleanChecksBundle = true + opts.CleanVEXRepositories = true } if opts.CleanScanCache { @@ -60,14 +63,6 @@ func Run(ctx context.Context, opts flag.Options) error { return nil } -func cleanAll(ctx context.Context, opts flag.Options) error { - log.InfoContext(ctx, "Removing all caches...") - if err := os.RemoveAll(opts.CacheDir); err != nil { - return xerrors.Errorf("failed to remove the directory (%s) : %w", opts.CacheDir, err) - } - return nil -} - func cleanScanCache(ctx context.Context, opts flag.Options) error { log.InfoContext(ctx, "Removing scan cache...") c, cleanup, err := cache.New(opts.CacheOpts()) diff --git a/pkg/commands/clean/run_test.go b/pkg/commands/clean/run_test.go index 9b301d238219..32e4110aba97 100644 --- a/pkg/commands/clean/run_test.go +++ b/pkg/commands/clean/run_test.go @@ -28,7 +28,12 @@ func TestRun(t *testing.T) { }, wantErr: false, checkFunc: func(t *testing.T, dir string) { - assert.NoDirExists(t, dir) + assert.NoDirExists(t, filepath.Join(dir, "fanal")) + assert.NoDirExists(t, filepath.Join(dir, "db")) + assert.NoDirExists(t, filepath.Join(dir, "java-db")) + assert.NoDirExists(t, filepath.Join(dir, "policy")) + assert.NoDirExists(t, filepath.Join(dir, "vex")) + assert.DirExists(t, dir) }, }, { @@ -42,6 +47,7 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "db")) assert.DirExists(t, filepath.Join(dir, "java-db")) assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -55,6 +61,7 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "fanal")) assert.DirExists(t, filepath.Join(dir, "java-db")) assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -68,6 +75,7 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "fanal")) assert.DirExists(t, filepath.Join(dir, "db")) assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -81,6 +89,21 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "fanal")) assert.DirExists(t, filepath.Join(dir, "db")) assert.DirExists(t, filepath.Join(dir, "java-db")) + assert.DirExists(t, filepath.Join(dir, "vex")) + }, + }, + { + name: "clean vex repositories", + cleanOpts: flag.CleanOptions{ + CleanVEXRepositories: true, + }, + wantErr: false, + checkFunc: func(t *testing.T, dir string) { + assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "fanal")) + assert.DirExists(t, filepath.Join(dir, "db")) + assert.DirExists(t, filepath.Join(dir, "java-db")) + assert.NoDirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -127,6 +150,7 @@ func createTestFiles(t *testing.T, dir string) { "db", "java-db", "policy", + "vex", } for _, subdir := range subdirs { err := os.MkdirAll(filepath.Join(dir, subdir), 0755) diff --git a/pkg/compliance/report/report_test.go b/pkg/compliance/report/report_test.go index e673bd50a24d..5481299b57ea 100644 --- a/pkg/compliance/report/report_test.go +++ b/pkg/compliance/report/report_test.go @@ -36,9 +36,8 @@ func TestBuildComplianceReport(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 0, - Exceptions: 0, + Successes: 1, + Failures: 0, }, Misconfigurations: []types.DetectedMisconfiguration{ { @@ -158,9 +157,8 @@ func TestBuildComplianceReport(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 0, - Exceptions: 0, + Successes: 1, + Failures: 0, }, Misconfigurations: []types.DetectedMisconfiguration{ { diff --git a/pkg/compliance/report/testdata/table_all.txt b/pkg/compliance/report/testdata/table_all.txt index 8a789b2ceaa5..159d5f8590ad 100644 --- a/pkg/compliance/report/testdata/table_all.txt +++ b/pkg/compliance/report/testdata/table_all.txt @@ -1,9 +1,9 @@ - -Deployment/metrics-server (kubernetes) -====================================== -Tests: 1 (SUCCESSES: 1, FAILURES: 0, EXCEPTIONS: 0) -Failures: 0 () - + +Deployment/metrics-server (kubernetes) +====================================== +Tests: 1 (SUCCESSES: 1, FAILURES: 0) +Failures: 0 () + MEDIUM: Container 'metrics-server' of Deployment 'metrics-server' should set 'securityContext.allowPrivilegeEscalation' to false ════════════════════════════════════════ A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node. @@ -24,12 +24,12 @@ See https://avd.aquasec.com/misconfig/ksv001 ──────────────────────────────────────── - -Deployment/metrics-server (kubernetes) -====================================== -Tests: 1 (SUCCESSES: 1, FAILURES: 0, EXCEPTIONS: 0) -Failures: 0 () - + +Deployment/metrics-server (kubernetes) +====================================== +Tests: 1 (SUCCESSES: 1, FAILURES: 0) +Failures: 0 () + LOW: Container 'metrics-server' of Deployment 'metrics-server' should add 'ALL' to 'securityContext.capabilities.drop' ════════════════════════════════════════ The container should drop all default capabilities and add only those that are needed for its execution. diff --git a/pkg/compliance/spec/mapper.go b/pkg/compliance/spec/mapper.go index 6fa5d2bbd45a..8c683753ad90 100644 --- a/pkg/compliance/spec/mapper.go +++ b/pkg/compliance/spec/mapper.go @@ -49,8 +49,6 @@ func misconfigSummary(misconfig types.DetectedMisconfiguration) *types.MisconfSu rms.Successes = 1 case types.MisconfStatusFailure: rms.Failures = 1 - case types.MisconfStatusException: - rms.Exceptions = 1 } return &rms } diff --git a/pkg/compliance/spec/mapper_test.go b/pkg/compliance/spec/mapper_test.go index 62050d27ec47..070c06a8ce96 100644 --- a/pkg/compliance/spec/mapper_test.go +++ b/pkg/compliance/spec/mapper_test.go @@ -61,9 +61,8 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 0, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ { @@ -79,9 +78,8 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 0, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ { diff --git a/pkg/dependency/id_test.go b/pkg/dependency/id_test.go index 18359f771e7b..2b8c0e4a71b1 100644 --- a/pkg/dependency/id_test.go +++ b/pkg/dependency/id_test.go @@ -34,7 +34,7 @@ func TestID(t *testing.T) { args: args{ ltype: types.GoModule, name: "test", - version: "1.0.0", + version: "v1.0.0", }, want: "test@v1.0.0", }, diff --git a/pkg/dependency/parser/golang/binary/parse.go b/pkg/dependency/parser/golang/binary/parse.go index 6959e0ede5fa..2a7a2a128de7 100644 --- a/pkg/dependency/parser/golang/binary/parse.go +++ b/pkg/dependency/parser/golang/binary/parse.go @@ -3,6 +3,7 @@ package binary import ( "cmp" "debug/buildinfo" + "fmt" "runtime/debug" "slices" "sort" @@ -56,6 +57,8 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // Ex: "go1.22.3 X:boringcrypto" stdlibVersion := strings.TrimPrefix(info.GoVersion, "go") stdlibVersion, _, _ = strings.Cut(stdlibVersion, " ") + // Add the `v` prefix to be consistent with module and dependency versions. + stdlibVersion = fmt.Sprintf("v%s", stdlibVersion) ldflags := p.ldFlags(info.Settings) pkgs := make(ftypes.Packages, 0, len(info.Deps)+2) diff --git a/pkg/dependency/parser/golang/binary/parse_test.go b/pkg/dependency/parser/golang/binary/parse_test.go index 2fbb6acff7b2..aade2a32cf24 100644 --- a/pkg/dependency/parser/golang/binary/parse_test.go +++ b/pkg/dependency/parser/golang/binary/parse_test.go @@ -20,7 +20,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.15.2", + Version: "v1.15.2", Relationship: ftypes.RelationshipDirect, }, { @@ -69,7 +69,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.16.4", + Version: "v1.16.4", Relationship: ftypes.RelationshipDirect, }, { @@ -93,7 +93,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.20.6", + Version: "v1.20.6", Relationship: ftypes.RelationshipDirect, }, }, @@ -109,7 +109,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", Relationship: ftypes.RelationshipDirect, }, }, @@ -120,7 +120,7 @@ func TestParse(t *testing.T) { want: []ftypes.Package{ { Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", Relationship: ftypes.RelationshipDirect, }, }, diff --git a/pkg/dependency/parser/golang/mod/parse.go b/pkg/dependency/parser/golang/mod/parse.go index bbf42926a766..ddcd2ccc880e 100644 --- a/pkg/dependency/parser/golang/mod/parse.go +++ b/pkg/dependency/parser/golang/mod/parse.go @@ -1,6 +1,7 @@ package mod import ( + "fmt" "io" "regexp" "strconv" @@ -90,9 +91,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc if p.useMinVersion { if toolchainVer := toolchainVersion(modFileParsed.Toolchain, modFileParsed.Go); toolchainVer != "" { pkgs["stdlib"] = ftypes.Package{ - ID: packageID("stdlib", toolchainVer), - Name: "stdlib", - Version: toolchainVer, + ID: packageID("stdlib", toolchainVer), + Name: "stdlib", + // Our versioning library doesn't support canonical (goX.Y.Z) format, + // So we need to add `v` prefix for consistency (with module and dependency versions). + Version: fmt.Sprintf("v%s", toolchainVer), Relationship: ftypes.RelationshipDirect, // Considered a direct dependency as the main module depends on the standard packages. } } @@ -100,11 +103,10 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // Main module if m := modFileParsed.Module; m != nil { - ver := strings.TrimPrefix(m.Mod.Version, "v") pkgs[m.Mod.Path] = ftypes.Package{ - ID: packageID(m.Mod.Path, ver), + ID: packageID(m.Mod.Path, m.Mod.Version), Name: m.Mod.Path, - Version: ver, + Version: m.Mod.Version, ExternalReferences: p.GetExternalRefs(m.Mod.Path), Relationship: ftypes.RelationshipRoot, } @@ -116,11 +118,10 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc if skipIndirect && require.Indirect { continue } - ver := strings.TrimPrefix(require.Mod.Version, "v") pkgs[require.Mod.Path] = ftypes.Package{ - ID: packageID(require.Mod.Path, ver), + ID: packageID(require.Mod.Path, require.Mod.Version), Name: require.Mod.Path, - Version: ver, + Version: require.Mod.Version, Relationship: lo.Ternary(require.Indirect, ftypes.RelationshipIndirect, ftypes.RelationshipDirect), ExternalReferences: p.GetExternalRefs(require.Mod.Path), } @@ -136,7 +137,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc } // If the replace directive has a version on the left side, make sure it matches the version that was imported. - if rep.Old.Version != "" && old.Version != rep.Old.Version[1:] { + if rep.Old.Version != "" && old.Version != rep.Old.Version { continue } @@ -153,9 +154,9 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // Add replaced package to package register. pkgs[rep.New.Path] = ftypes.Package{ - ID: packageID(rep.New.Path, rep.New.Version[1:]), + ID: packageID(rep.New.Path, rep.New.Version), Name: rep.New.Path, - Version: rep.New.Version[1:], + Version: rep.New.Version, Relationship: old.Relationship, ExternalReferences: p.GetExternalRefs(rep.New.Path), } diff --git a/pkg/dependency/parser/golang/mod/parse_testcase.go b/pkg/dependency/parser/golang/mod/parse_testcase.go index 4671ef3e6854..b8ed49008926 100644 --- a/pkg/dependency/parser/golang/mod/parse_testcase.go +++ b/pkg/dependency/parser/golang/mod/parse_testcase.go @@ -23,13 +23,13 @@ var ( { ID: "stdlib@v1.22.5", Name: "stdlib", - Version: "1.22.5", + Version: "v1.22.5", Relationship: ftypes.RelationshipDirect, }, { ID: "github.com/aquasecurity/go-version@v0.0.0-20240603093900-cf8a8d29271d", Name: "github.com/aquasecurity/go-version", - Version: "0.0.0-20240603093900-cf8a8d29271d", + Version: "v0.0.0-20240603093900-cf8a8d29271d", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -41,7 +41,7 @@ var ( { ID: "github.com/davecgh/go-spew@v1.1.2-0.20180830191138-d8f796af33cc", Name: "github.com/davecgh/go-spew", - Version: "1.1.2-0.20180830191138-d8f796af33cc", + Version: "v1.1.2-0.20180830191138-d8f796af33cc", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -53,7 +53,7 @@ var ( { ID: "github.com/pmezard/go-difflib@v1.0.1-0.20181226105442-5d4384ee4fb2", Name: "github.com/pmezard/go-difflib", - Version: "1.0.1-0.20181226105442-5d4384ee4fb2", + Version: "v1.0.1-0.20181226105442-5d4384ee4fb2", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -65,7 +65,7 @@ var ( { ID: "github.com/stretchr/testify@v1.9.0", Name: "github.com/stretchr/testify", - Version: "1.9.0", + Version: "v1.9.0", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -77,7 +77,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20231012003039-104605ab7028", Name: "golang.org/x/xerrors", - Version: "0.0.0-20231012003039-104605ab7028", + Version: "v0.0.0-20231012003039-104605ab7028", Relationship: ftypes.RelationshipIndirect, }, } @@ -102,7 +102,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20220406074731-71021a481237", + Version: "v0.0.0-20220406074731-71021a481237", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -114,7 +114,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, } @@ -135,7 +135,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211110174639-8257534ffed3", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211110174639-8257534ffed3", + Version: "v0.0.0-20211110174639-8257534ffed3", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -147,7 +147,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, } @@ -168,7 +168,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20220406074731-71021a481237", + Version: "v0.0.0-20220406074731-71021a481237", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -180,7 +180,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, } @@ -201,7 +201,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -213,13 +213,13 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -246,7 +246,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -258,7 +258,7 @@ var ( { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -285,7 +285,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -297,7 +297,7 @@ var ( { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -324,7 +324,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -336,13 +336,13 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -369,7 +369,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -396,7 +396,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { diff --git a/pkg/dependency/parser/golang/sum/parse.go b/pkg/dependency/parser/golang/sum/parse.go index 4ec742b1bae2..8362a808c036 100644 --- a/pkg/dependency/parser/golang/sum/parse.go +++ b/pkg/dependency/parser/golang/sum/parse.go @@ -32,7 +32,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // go.sum records and sorts all non-major versions // with the latest version as last entry - uniquePkgs[s[0]] = strings.TrimSuffix(strings.TrimPrefix(s[1], "v"), "/go.mod") + uniquePkgs[s[0]] = strings.TrimSuffix(s[1], "/go.mod") } if err := scanner.Err(); err != nil { return nil, nil, xerrors.Errorf("scan error: %w", err) diff --git a/pkg/dependency/parser/golang/sum/parse_testcase.go b/pkg/dependency/parser/golang/sum/parse_testcase.go index fc607de86e2a..532a241b1ccc 100644 --- a/pkg/dependency/parser/golang/sum/parse_testcase.go +++ b/pkg/dependency/parser/golang/sum/parse_testcase.go @@ -11,13 +11,13 @@ var ( // go get golang.org/x/xerrors // go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}' GoModNormal = []ftypes.Package{ - {Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"}, + {Name: "golang.org/x/xerrors", Version: "v0.0.0-20200804184101-5ec99f83aff1"}, } // https://github.com/uudashr/gopkgs/blob/616744904701ef01d868da4b66aad0e6856c361d/v2/go.sum GoModEmptyLine = []ftypes.Package{ - {Name: "github.com/karrick/godirwalk", Version: "1.12.0"}, - {Name: "github.com/pkg/errors", Version: "0.8.1"}, + {Name: "github.com/karrick/godirwalk", Version: "v1.12.0"}, + {Name: "github.com/pkg/errors", Version: "v0.8.1"}, } // docker run --name gomod --rm -it golang:1.15 bash @@ -31,19 +31,19 @@ var ( // go get github.com/BurntSushi/toml // go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}' GoModMany = []ftypes.Package{ - {Name: "github.com/BurntSushi/toml", Version: "0.3.1"}, - {Name: "github.com/cpuguy83/go-md2man/v2", Version: "2.0.0-20190314233015-f79a8a8ca69d"}, - {Name: "github.com/davecgh/go-spew", Version: "1.1.0"}, - {Name: "github.com/pmezard/go-difflib", Version: "1.0.0"}, - {Name: "github.com/russross/blackfriday/v2", Version: "2.0.1"}, - {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "1.0.0"}, - {Name: "github.com/stretchr/objx", Version: "0.1.0"}, - {Name: "github.com/stretchr/testify", Version: "1.7.0"}, - {Name: "github.com/urfave/cli", Version: "1.22.5"}, - {Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"}, - {Name: "gopkg.in/check.v1", Version: "0.0.0-20161208181325-20d25e280405"}, - {Name: "gopkg.in/yaml.v2", Version: "2.2.2"}, - {Name: "gopkg.in/yaml.v3", Version: "3.0.0-20200313102051-9f266ea9e77c"}, + {Name: "github.com/BurntSushi/toml", Version: "v0.3.1"}, + {Name: "github.com/cpuguy83/go-md2man/v2", Version: "v2.0.0-20190314233015-f79a8a8ca69d"}, + {Name: "github.com/davecgh/go-spew", Version: "v1.1.0"}, + {Name: "github.com/pmezard/go-difflib", Version: "v1.0.0"}, + {Name: "github.com/russross/blackfriday/v2", Version: "v2.0.1"}, + {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "v1.0.0"}, + {Name: "github.com/stretchr/objx", Version: "v0.1.0"}, + {Name: "github.com/stretchr/testify", Version: "v1.7.0"}, + {Name: "github.com/urfave/cli", Version: "v1.22.5"}, + {Name: "golang.org/x/xerrors", Version: "v0.0.0-20200804184101-5ec99f83aff1"}, + {Name: "gopkg.in/check.v1", Version: "v0.0.0-20161208181325-20d25e280405"}, + {Name: "gopkg.in/yaml.v2", Version: "v2.2.2"}, + {Name: "gopkg.in/yaml.v3", Version: "v3.0.0-20200313102051-9f266ea9e77c"}, } // docker run --name gomod --rm -it golang:1.15 bash @@ -54,341 +54,341 @@ var ( // go get github.com/aquasecurity/trivy // go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}' GoModTrivy = []ftypes.Package{ - {Name: "cloud.google.com/go", Version: "0.65.0"}, - {Name: "cloud.google.com/go/bigquery", Version: "1.8.0"}, - {Name: "cloud.google.com/go/datastore", Version: "1.1.0"}, - {Name: "cloud.google.com/go/pubsub", Version: "1.3.1"}, - {Name: "cloud.google.com/go/storage", Version: "1.10.0"}, - {Name: "dmitri.shuralyov.com/gpu/mtl", Version: "0.0.0-20190408044501-666a987793e9"}, - {Name: "github.com/Azure/azure-sdk-for-go", Version: "38.0.0+incompatible"}, - {Name: "github.com/Azure/go-ansiterm", Version: "0.0.0-20170929234023-d6e3b3328b78"}, - {Name: "github.com/Azure/go-autorest/autorest", Version: "0.9.3"}, - {Name: "github.com/Azure/go-autorest/autorest/adal", Version: "0.8.1"}, - {Name: "github.com/Azure/go-autorest/autorest/date", Version: "0.2.0"}, - {Name: "github.com/Azure/go-autorest/autorest/mocks", Version: "0.3.0"}, - {Name: "github.com/Azure/go-autorest/autorest/to", Version: "0.3.0"}, - {Name: "github.com/Azure/go-autorest/autorest/validation", Version: "0.1.0"}, - {Name: "github.com/Azure/go-autorest/logger", Version: "0.1.0"}, - {Name: "github.com/Azure/go-autorest/tracing", Version: "0.5.0"}, - {Name: "github.com/BurntSushi/toml", Version: "0.3.1"}, - {Name: "github.com/BurntSushi/xgb", Version: "0.0.0-20160522181843-27f122750802"}, - {Name: "github.com/GoogleCloudPlatform/docker-credential-gcr", Version: "1.5.0"}, - {Name: "github.com/GoogleCloudPlatform/k8s-cloud-provider", Version: "0.0.0-20190822182118-27a4ced34534"}, - {Name: "github.com/Microsoft/go-winio", Version: "0.4.15-0.20190919025122-fc70bd9a86b5"}, - {Name: "github.com/Microsoft/hcsshim", Version: "0.8.6"}, - {Name: "github.com/NYTimes/gziphandler", Version: "0.0.0-20170623195520-56545f4a5d46"}, - {Name: "github.com/OneOfOne/xxhash", Version: "1.2.7"}, - {Name: "github.com/PuerkitoBio/purell", Version: "1.1.1"}, - {Name: "github.com/PuerkitoBio/urlesc", Version: "0.0.0-20170810143723-de5bf2ad4578"}, - {Name: "github.com/VividCortex/ewma", Version: "1.1.1"}, - {Name: "github.com/alcortesm/tgz", Version: "0.0.0-20161220082320-9c5fe88206d7"}, - {Name: "github.com/alecthomas/template", Version: "0.0.0-20160405071501-a0175ee3bccc"}, - {Name: "github.com/alecthomas/units", Version: "0.0.0-20151022065526-2efee857e7cf"}, - {Name: "github.com/alicebob/gopher-json", Version: "0.0.0-20200520072559-a9ecdc9d1d3a"}, - {Name: "github.com/alicebob/miniredis/v2", Version: "2.14.1"}, - {Name: "github.com/anmitsu/go-shlex", Version: "0.0.0-20161002113705-648efa622239"}, - {Name: "github.com/aquasecurity/bolt-fixtures", Version: "0.0.0-20200903104109-d34e7f983986"}, - {Name: "github.com/aquasecurity/fanal", Version: "0.0.0-20210119051230-28c249da7cfd"}, - {Name: "github.com/aquasecurity/go-dep-parser", Version: "0.0.0-20201028043324-889d4a92b8e0"}, - {Name: "github.com/aquasecurity/go-gem-version", Version: "0.0.0-20201115065557-8eed6fe000ce"}, - {Name: "github.com/aquasecurity/go-npm-version", Version: "0.0.0-20201110091526-0b796d180798"}, - {Name: "github.com/aquasecurity/go-pep440-version", Version: "0.0.0-20210121094942-22b2f8951d46"}, - {Name: "github.com/aquasecurity/go-version", Version: "0.0.0-20210121072130-637058cfe492"}, - {Name: "github.com/aquasecurity/testdocker", Version: "0.0.0-20210106133225-0b17fe083674"}, - {Name: "github.com/aquasecurity/trivy", Version: "0.16.0"}, - {Name: "github.com/aquasecurity/trivy-db", Version: "0.0.0-20210105160501-c5bf4e153277"}, - {Name: "github.com/aquasecurity/vuln-list-update", Version: "0.0.0-20191016075347-3d158c2bf9a2"}, - {Name: "github.com/araddon/dateparse", Version: "0.0.0-20190426192744-0d74ffceef83"}, - {Name: "github.com/armon/consul-api", Version: "0.0.0-20180202201655-eb2c6b5be1b6"}, - {Name: "github.com/armon/go-socks5", Version: "0.0.0-20160902184237-e75332964ef5"}, - {Name: "github.com/aws/aws-sdk-go", Version: "1.27.1"}, - {Name: "github.com/beorn7/perks", Version: "1.0.0"}, - {Name: "github.com/bgentry/speakeasy", Version: "0.1.0"}, - {Name: "github.com/blang/semver", Version: "3.5.0+incompatible"}, - {Name: "github.com/briandowns/spinner", Version: "1.12.0"}, - {Name: "github.com/caarlos0/env/v6", Version: "6.0.0"}, - {Name: "github.com/cenkalti/backoff", Version: "2.2.1+incompatible"}, - {Name: "github.com/census-instrumentation/opencensus-proto", Version: "0.2.1"}, - {Name: "github.com/cespare/xxhash/v2", Version: "2.1.1"}, - {Name: "github.com/cheggaaa/pb/v3", Version: "3.0.3"}, - {Name: "github.com/chzyer/logex", Version: "1.1.10"}, - {Name: "github.com/chzyer/readline", Version: "0.0.0-20180603132655-2972be24d48e"}, - {Name: "github.com/chzyer/test", Version: "0.0.0-20180213035817-a1ea475d72b1"}, - {Name: "github.com/client9/misspell", Version: "0.3.4"}, - {Name: "github.com/cncf/udpa/go", Version: "0.0.0-20191209042840-269d4d468f6f"}, - {Name: "github.com/cockroachdb/datadriven", Version: "0.0.0-20190809214429-80d97fb3cbaa"}, - {Name: "github.com/containerd/containerd", Version: "1.3.3"}, - {Name: "github.com/containerd/continuity", Version: "0.0.0-20190426062206-aaeac12a7ffc"}, - {Name: "github.com/coreos/etcd", Version: "3.3.10+incompatible"}, - {Name: "github.com/coreos/go-etcd", Version: "2.0.0+incompatible"}, - {Name: "github.com/coreos/go-oidc", Version: "2.1.0+incompatible"}, - {Name: "github.com/coreos/go-semver", Version: "0.3.0"}, - {Name: "github.com/coreos/go-systemd", Version: "0.0.0-20190321100706-95778dfbb74e"}, - {Name: "github.com/coreos/pkg", Version: "0.0.0-20180108230652-97fdf19511ea"}, - {Name: "github.com/cpuguy83/go-md2man", Version: "1.0.10"}, - {Name: "github.com/cpuguy83/go-md2man/v2", Version: "2.0.0"}, - {Name: "github.com/creack/pty", Version: "1.1.9"}, - {Name: "github.com/davecgh/go-spew", Version: "1.1.1"}, - {Name: "github.com/deckarep/golang-set", Version: "1.7.1"}, - {Name: "github.com/dgrijalva/jwt-go", Version: "3.2.0+incompatible"}, - {Name: "github.com/dgryski/go-rendezvous", Version: "0.0.0-20200823014737-9f7001d12a5f"}, - {Name: "github.com/dnaeon/go-vcr", Version: "1.0.1"}, - {Name: "github.com/docker/cli", Version: "0.0.0-20191017083524-a8ff7f821017"}, - {Name: "github.com/docker/distribution", Version: "2.7.1+incompatible"}, - {Name: "github.com/docker/docker", Version: "1.4.2-0.20190924003213-a8608b5b67c7"}, - {Name: "github.com/docker/docker-credential-helpers", Version: "0.6.3"}, - {Name: "github.com/docker/go-connections", Version: "0.4.0"}, - {Name: "github.com/docker/go-units", Version: "0.4.0"}, - {Name: "github.com/docker/spdystream", Version: "0.0.0-20160310174837-449fdfce4d96"}, - {Name: "github.com/dustin/go-humanize", Version: "1.0.0"}, - {Name: "github.com/elazarl/goproxy", Version: "0.0.0-20200809112317-0581fc3aee2d"}, - {Name: "github.com/elazarl/goproxy/ext", Version: "0.0.0-20200809112317-0581fc3aee2d"}, - {Name: "github.com/emicklei/go-restful", Version: "2.9.5+incompatible"}, - {Name: "github.com/emirpasic/gods", Version: "1.12.0"}, - {Name: "github.com/envoyproxy/go-control-plane", Version: "0.9.4"}, - {Name: "github.com/envoyproxy/protoc-gen-validate", Version: "0.1.0"}, - {Name: "github.com/evanphx/json-patch", Version: "4.2.0+incompatible"}, - {Name: "github.com/fatih/color", Version: "1.10.0"}, - {Name: "github.com/flynn/go-shlex", Version: "0.0.0-20150515145356-3f9db97f8568"}, - {Name: "github.com/fsnotify/fsnotify", Version: "1.4.9"}, - {Name: "github.com/ghodss/yaml", Version: "1.0.0"}, - {Name: "github.com/gin-contrib/sse", Version: "0.1.0"}, - {Name: "github.com/gin-gonic/gin", Version: "1.5.0"}, - {Name: "github.com/gliderlabs/ssh", Version: "0.2.2"}, - {Name: "github.com/go-git/gcfg", Version: "1.5.0"}, - {Name: "github.com/go-git/go-billy/v5", Version: "5.0.0"}, - {Name: "github.com/go-git/go-git-fixtures/v4", Version: "4.0.1"}, - {Name: "github.com/go-git/go-git/v5", Version: "5.0.0"}, - {Name: "github.com/go-gl/glfw", Version: "0.0.0-20190409004039-e6da0acd62b1"}, - {Name: "github.com/go-gl/glfw/v3.3/glfw", Version: "0.0.0-20200222043503-6f7a984d4dc4"}, - {Name: "github.com/go-kit/kit", Version: "0.8.0"}, - {Name: "github.com/go-logfmt/logfmt", Version: "0.3.0"}, - {Name: "github.com/go-logr/logr", Version: "0.1.0"}, - {Name: "github.com/go-openapi/jsonpointer", Version: "0.19.3"}, - {Name: "github.com/go-openapi/jsonreference", Version: "0.19.3"}, - {Name: "github.com/go-openapi/spec", Version: "0.19.3"}, - {Name: "github.com/go-openapi/swag", Version: "0.19.5"}, - {Name: "github.com/go-playground/locales", Version: "0.13.0"}, - {Name: "github.com/go-playground/universal-translator", Version: "0.17.0"}, - {Name: "github.com/go-redis/redis", Version: "6.15.7+incompatible"}, - {Name: "github.com/go-redis/redis/v8", Version: "8.4.0"}, - {Name: "github.com/go-restruct/restruct", Version: "0.0.0-20191227155143-5734170a48a1"}, - {Name: "github.com/go-sql-driver/mysql", Version: "1.5.0"}, - {Name: "github.com/go-stack/stack", Version: "1.8.0"}, - {Name: "github.com/gobwas/glob", Version: "0.2.3"}, - {Name: "github.com/goccy/go-yaml", Version: "1.8.2"}, - {Name: "github.com/gogo/protobuf", Version: "1.3.1"}, - {Name: "github.com/golang/glog", Version: "0.0.0-20160126235308-23def4e6c14b"}, - {Name: "github.com/golang/groupcache", Version: "0.0.0-20200121045136-8c9f03a8e57e"}, - {Name: "github.com/golang/mock", Version: "1.4.4"}, - {Name: "github.com/golang/protobuf", Version: "1.4.2"}, - {Name: "github.com/google/btree", Version: "1.0.0"}, - {Name: "github.com/google/go-cmp", Version: "0.5.3"}, - {Name: "github.com/google/go-containerregistry", Version: "0.0.0-20200331213917-3d03ed9b1ca2"}, - {Name: "github.com/google/go-github/v28", Version: "28.1.1"}, - {Name: "github.com/google/go-querystring", Version: "1.0.0"}, - {Name: "github.com/google/gofuzz", Version: "1.0.0"}, - {Name: "github.com/google/martian", Version: "2.1.0+incompatible"}, - {Name: "github.com/google/martian/v3", Version: "3.0.0"}, - {Name: "github.com/google/pprof", Version: "0.0.0-20200708004538-1a94d8640e99"}, - {Name: "github.com/google/renameio", Version: "0.1.0"}, - {Name: "github.com/google/subcommands", Version: "1.0.1"}, - {Name: "github.com/google/uuid", Version: "1.1.1"}, - {Name: "github.com/google/wire", Version: "0.3.0"}, - {Name: "github.com/googleapis/gax-go/v2", Version: "2.0.5"}, - {Name: "github.com/googleapis/gnostic", Version: "0.2.2"}, - {Name: "github.com/gophercloud/gophercloud", Version: "0.1.0"}, - {Name: "github.com/gopherjs/gopherjs", Version: "0.0.0-20200217142428-fce0ec30dd00"}, - {Name: "github.com/gorilla/context", Version: "1.1.1"}, - {Name: "github.com/gorilla/mux", Version: "1.7.4"}, - {Name: "github.com/gorilla/websocket", Version: "1.4.0"}, - {Name: "github.com/gregjones/httpcache", Version: "0.0.0-20180305231024-9cad4c3443a7"}, - {Name: "github.com/grpc-ecosystem/go-grpc-middleware", Version: "1.0.1-0.20190118093823-f849b5445de4"}, - {Name: "github.com/grpc-ecosystem/go-grpc-prometheus", Version: "1.2.0"}, - {Name: "github.com/grpc-ecosystem/grpc-gateway", Version: "1.9.5"}, - {Name: "github.com/hashicorp/errwrap", Version: "1.0.0"}, - {Name: "github.com/hashicorp/go-multierror", Version: "1.1.0"}, - {Name: "github.com/hashicorp/go-version", Version: "1.2.1"}, - {Name: "github.com/hashicorp/golang-lru", Version: "0.5.3"}, - {Name: "github.com/hashicorp/hcl", Version: "1.0.0"}, - {Name: "github.com/hpcloud/tail", Version: "1.0.0"}, - {Name: "github.com/ianlancetaylor/demangle", Version: "0.0.0-20181102032728-5e5cf60278f6"}, - {Name: "github.com/imdario/mergo", Version: "0.3.5"}, - {Name: "github.com/inconshreveable/mousetrap", Version: "1.0.0"}, - {Name: "github.com/jbenet/go-context", Version: "0.0.0-20150711004518-d14ea06fba99"}, - {Name: "github.com/jessevdk/go-flags", Version: "1.4.0"}, - {Name: "github.com/jmespath/go-jmespath", Version: "0.0.0-20180206201540-c2b33e8439af"}, - {Name: "github.com/joefitzgerald/rainbow-reporter", Version: "0.1.0"}, - {Name: "github.com/jonboulle/clockwork", Version: "0.1.0"}, - {Name: "github.com/json-iterator/go", Version: "1.1.8"}, - {Name: "github.com/jstemmer/go-junit-report", Version: "0.9.1"}, - {Name: "github.com/jtolds/gls", Version: "4.20.0+incompatible"}, - {Name: "github.com/julienschmidt/httprouter", Version: "1.2.0"}, - {Name: "github.com/kevinburke/ssh_config", Version: "0.0.0-20190725054713-01f96b0aa0cd"}, - {Name: "github.com/kisielk/errcheck", Version: "1.2.0"}, - {Name: "github.com/kisielk/gotool", Version: "1.0.0"}, - {Name: "github.com/knqyf263/go-apk-version", Version: "0.0.0-20200609155635-041fdbb8563f"}, - {Name: "github.com/knqyf263/go-deb-version", Version: "0.0.0-20190517075300-09fca494f03d"}, - {Name: "github.com/knqyf263/go-rpm-version", Version: "0.0.0-20170716094938-74609b86c936"}, - {Name: "github.com/knqyf263/go-rpmdb", Version: "0.0.0-20201215100354-a9e3110d8ee1"}, - {Name: "github.com/knqyf263/nested", Version: "0.0.1"}, - {Name: "github.com/konsorten/go-windows-terminal-sequences", Version: "1.0.2"}, - {Name: "github.com/kr/logfmt", Version: "0.0.0-20140226030751-b84e30acd515"}, - {Name: "github.com/kr/pretty", Version: "0.1.0"}, - {Name: "github.com/kr/pty", Version: "1.1.5"}, - {Name: "github.com/kr/text", Version: "0.2.0"}, - {Name: "github.com/kylelemons/godebug", Version: "1.1.0"}, - {Name: "github.com/leodido/go-urn", Version: "1.2.0"}, - {Name: "github.com/magiconair/properties", Version: "1.8.0"}, - {Name: "github.com/mailru/easyjson", Version: "0.7.0"}, - {Name: "github.com/mattn/go-colorable", Version: "0.1.8"}, - {Name: "github.com/mattn/go-isatty", Version: "0.0.12"}, - {Name: "github.com/mattn/go-jsonpointer", Version: "0.0.0-20180225143300-37667080efed"}, - {Name: "github.com/mattn/go-runewidth", Version: "0.0.9"}, - {Name: "github.com/matttproud/golang_protobuf_extensions", Version: "1.0.1"}, - {Name: "github.com/maxbrunsfeld/counterfeiter/v6", Version: "6.2.2"}, - {Name: "github.com/mitchellh/go-homedir", Version: "1.1.0"}, - {Name: "github.com/mitchellh/mapstructure", Version: "1.1.2"}, - {Name: "github.com/modern-go/concurrent", Version: "0.0.0-20180306012644-bacd9c7ef1dd"}, - {Name: "github.com/modern-go/reflect2", Version: "1.0.1"}, - {Name: "github.com/morikuni/aec", Version: "1.0.0"}, - {Name: "github.com/munnerz/goautoneg", Version: "0.0.0-20191010083416-a7dc8b61c822"}, - {Name: "github.com/mwitkow/go-conntrack", Version: "0.0.0-20161129095857-cc309e4a2223"}, - {Name: "github.com/mxk/go-flowrate", Version: "0.0.0-20140419014527-cca7078d478f"}, - {Name: "github.com/niemeyer/pretty", Version: "0.0.0-20200227124842-a10e7caefd8e"}, - {Name: "github.com/nxadm/tail", Version: "1.4.4"}, - {Name: "github.com/olekukonko/tablewriter", Version: "0.0.2-0.20190607075207-195002e6e56a"}, - {Name: "github.com/onsi/ginkgo", Version: "1.14.2"}, - {Name: "github.com/onsi/gomega", Version: "1.10.3"}, - {Name: "github.com/open-policy-agent/opa", Version: "0.21.1"}, - {Name: "github.com/opencontainers/go-digest", Version: "1.0.0-rc1"}, - {Name: "github.com/opencontainers/image-spec", Version: "1.0.2-0.20190823105129-775207bd45b6"}, - {Name: "github.com/opencontainers/runc", Version: "0.1.1"}, - {Name: "github.com/parnurzeal/gorequest", Version: "0.2.16"}, - {Name: "github.com/pelletier/go-toml", Version: "1.2.0"}, - {Name: "github.com/peterbourgon/diskv", Version: "2.0.1+incompatible"}, - {Name: "github.com/peterh/liner", Version: "0.0.0-20170211195444-bf27d3ba8e1d"}, - {Name: "github.com/pkg/errors", Version: "0.9.1"}, - {Name: "github.com/pmezard/go-difflib", Version: "1.0.0"}, - {Name: "github.com/pquerna/cachecontrol", Version: "0.0.0-20171018203845-0dec1b30a021"}, - {Name: "github.com/prometheus/client_golang", Version: "1.0.0"}, - {Name: "github.com/prometheus/client_model", Version: "0.0.0-20190812154241-14fe0d1b01d4"}, - {Name: "github.com/prometheus/common", Version: "0.4.1"}, - {Name: "github.com/prometheus/procfs", Version: "0.0.2"}, - {Name: "github.com/rcrowley/go-metrics", Version: "0.0.0-20181016184325-3113b8401b8a"}, - {Name: "github.com/remyoudompheng/bigfft", Version: "0.0.0-20170806203942-52369c62f446"}, - {Name: "github.com/rogpeppe/fastuuid", Version: "0.0.0-20150106093220-6724a57986af"}, - {Name: "github.com/rogpeppe/go-charset", Version: "0.0.0-20180617210344-2471d30d28b4"}, - {Name: "github.com/rogpeppe/go-internal", Version: "1.3.0"}, - {Name: "github.com/rubiojr/go-vhd", Version: "0.0.0-20160810183302-0bfd3b39853c"}, - {Name: "github.com/russross/blackfriday", Version: "1.5.2"}, - {Name: "github.com/russross/blackfriday/v2", Version: "2.0.1"}, - {Name: "github.com/saracen/walker", Version: "0.0.0-20191201085201-324a081bae7e"}, - {Name: "github.com/satori/go.uuid", Version: "1.2.0"}, - {Name: "github.com/sclevine/spec", Version: "1.2.0"}, - {Name: "github.com/sergi/go-diff", Version: "1.1.0"}, - {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "1.0.0"}, - {Name: "github.com/simplereach/timeutils", Version: "1.2.0"}, - {Name: "github.com/sirupsen/logrus", Version: "1.5.0"}, - {Name: "github.com/smartystreets/assertions", Version: "1.2.0"}, - {Name: "github.com/smartystreets/goconvey", Version: "1.6.4"}, - {Name: "github.com/soheilhy/cmux", Version: "0.1.4"}, - {Name: "github.com/sosedoff/gitkit", Version: "0.2.0"}, - {Name: "github.com/spf13/afero", Version: "1.2.2"}, - {Name: "github.com/spf13/cast", Version: "1.3.0"}, - {Name: "github.com/spf13/cobra", Version: "0.0.5"}, - {Name: "github.com/spf13/jwalterweatherman", Version: "1.0.0"}, - {Name: "github.com/spf13/pflag", Version: "1.0.5"}, - {Name: "github.com/spf13/viper", Version: "1.3.2"}, - {Name: "github.com/stretchr/objx", Version: "0.3.0"}, - {Name: "github.com/stretchr/testify", Version: "1.6.1"}, - {Name: "github.com/testcontainers/testcontainers-go", Version: "0.3.1"}, - {Name: "github.com/tmc/grpc-websocket-proxy", Version: "0.0.0-20170815181823-89b8d40f7ca8"}, - {Name: "github.com/twitchtv/twirp", Version: "5.10.1+incompatible"}, - {Name: "github.com/ugorji/go", Version: "1.1.7"}, - {Name: "github.com/ugorji/go/codec", Version: "1.1.7"}, - {Name: "github.com/urfave/cli", Version: "1.22.5"}, - {Name: "github.com/urfave/cli/v2", Version: "2.3.0"}, - {Name: "github.com/vdemeester/k8s-pkg-credentialprovider", Version: "1.17.4"}, - {Name: "github.com/vmware/govmomi", Version: "0.20.3"}, - {Name: "github.com/xanzy/ssh-agent", Version: "0.2.1"}, - {Name: "github.com/xiang90/probing", Version: "0.0.0-20190116061207-43a291ad63a2"}, - {Name: "github.com/xordataexchange/crypt", Version: "0.0.3-0.20170626215501-b2862e3d0a77"}, - {Name: "github.com/yashtewari/glob-intersection", Version: "0.0.0-20180916065949-5c77d914dd0b"}, - {Name: "github.com/yuin/goldmark", Version: "1.1.32"}, - {Name: "github.com/yuin/gopher-lua", Version: "0.0.0-20191220021717-ab39c6098bdb"}, - {Name: "go.etcd.io/bbolt", Version: "1.3.5"}, - {Name: "go.etcd.io/etcd", Version: "0.0.0-20191023171146-3cf2f69b5738"}, - {Name: "go.opencensus.io", Version: "0.22.4"}, - {Name: "go.opentelemetry.io/otel", Version: "0.14.0"}, - {Name: "go.uber.org/atomic", Version: "1.5.1"}, - {Name: "go.uber.org/multierr", Version: "1.4.0"}, - {Name: "go.uber.org/tools", Version: "0.0.0-20190618225709-2cfd321de3ee"}, - {Name: "go.uber.org/zap", Version: "1.13.0"}, - {Name: "golang.org/x/crypto", Version: "0.0.0-20201002170205-7f63de1d35b0"}, - {Name: "golang.org/x/exp", Version: "0.0.0-20200224162631-6cc2880d07d6"}, - {Name: "golang.org/x/image", Version: "0.0.0-20190802002840-cff245a6509b"}, - {Name: "golang.org/x/lint", Version: "0.0.0-20200302205851-738671d3881b"}, - {Name: "golang.org/x/mobile", Version: "0.0.0-20190719004257-d2bd2a29d028"}, - {Name: "golang.org/x/mod", Version: "0.3.0"}, - {Name: "golang.org/x/net", Version: "0.0.0-20201006153459-a7d1128ccaa0"}, - {Name: "golang.org/x/oauth2", Version: "0.0.0-20201208152858-08078c50e5b5"}, - {Name: "golang.org/x/sync", Version: "0.0.0-20200625203802-6e8e738ad208"}, - {Name: "golang.org/x/sys", Version: "0.0.0-20201006155630-ac719f4daadf"}, - {Name: "golang.org/x/text", Version: "0.3.3"}, - {Name: "golang.org/x/time", Version: "0.0.0-20191024005414-555d28b269f0"}, - {Name: "golang.org/x/tools", Version: "0.0.0-20200825202427-b303f430e36d"}, - {Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"}, - {Name: "gonum.org/v1/gonum", Version: "0.0.0-20190331200053-3d26580ed485"}, - {Name: "gonum.org/v1/netlib", Version: "0.0.0-20190331212654-76723241ea4e"}, - {Name: "google.golang.org/api", Version: "0.30.0"}, - {Name: "google.golang.org/appengine", Version: "1.6.6"}, - {Name: "google.golang.org/genproto", Version: "0.0.0-20200825200019-8632dd797987"}, - {Name: "google.golang.org/grpc", Version: "1.31.0"}, - {Name: "google.golang.org/protobuf", Version: "1.25.0"}, - {Name: "gopkg.in/alecthomas/kingpin.v2", Version: "2.2.6"}, - {Name: "gopkg.in/check.v1", Version: "1.0.0-20200902074654-038fdea0a05b"}, - {Name: "gopkg.in/cheggaaa/pb.v1", Version: "1.0.28"}, - {Name: "gopkg.in/errgo.v2", Version: "2.1.0"}, - {Name: "gopkg.in/fsnotify.v1", Version: "1.4.7"}, - {Name: "gopkg.in/gcfg.v1", Version: "1.2.0"}, - {Name: "gopkg.in/go-playground/assert.v1", Version: "1.2.1"}, - {Name: "gopkg.in/go-playground/validator.v9", Version: "9.31.0"}, - {Name: "gopkg.in/inf.v0", Version: "0.9.1"}, - {Name: "gopkg.in/mgo.v2", Version: "2.0.0-20180705113604-9856a29383ce"}, - {Name: "gopkg.in/natefinch/lumberjack.v2", Version: "2.0.0"}, - {Name: "gopkg.in/resty.v1", Version: "1.12.0"}, - {Name: "gopkg.in/square/go-jose.v2", Version: "2.2.2"}, - {Name: "gopkg.in/tomb.v1", Version: "1.0.0-20141024135613-dd632973f1e7"}, - {Name: "gopkg.in/warnings.v0", Version: "0.1.2"}, - {Name: "gopkg.in/yaml.v2", Version: "2.4.0"}, - {Name: "gopkg.in/yaml.v3", Version: "3.0.0-20200615113413-eeeca48fe776"}, - {Name: "gotest.tools", Version: "2.2.0+incompatible"}, - {Name: "honnef.co/go/tools", Version: "0.0.1-2020.1.4"}, - {Name: "k8s.io/api", Version: "0.17.4"}, - {Name: "k8s.io/apimachinery", Version: "0.17.4"}, - {Name: "k8s.io/apiserver", Version: "0.17.4"}, - {Name: "k8s.io/client-go", Version: "0.17.4"}, - {Name: "k8s.io/cloud-provider", Version: "0.17.4"}, - {Name: "k8s.io/code-generator", Version: "0.17.2"}, - {Name: "k8s.io/component-base", Version: "0.17.4"}, - {Name: "k8s.io/csi-translation-lib", Version: "0.17.4"}, - {Name: "k8s.io/gengo", Version: "0.0.0-20190822140433-26a664648505"}, - {Name: "k8s.io/klog", Version: "1.0.0"}, - {Name: "k8s.io/klog/v2", Version: "2.0.0"}, - {Name: "k8s.io/kube-openapi", Version: "0.0.0-20191107075043-30be4d16710a"}, - {Name: "k8s.io/legacy-cloud-providers", Version: "0.17.4"}, - {Name: "k8s.io/utils", Version: "0.0.0-20201110183641-67b214c5f920"}, - {Name: "modernc.org/cc", Version: "1.0.0"}, - {Name: "modernc.org/golex", Version: "1.0.0"}, - {Name: "modernc.org/mathutil", Version: "1.0.0"}, - {Name: "modernc.org/strutil", Version: "1.0.0"}, - {Name: "modernc.org/xc", Version: "1.0.0"}, - {Name: "moul.io/http2curl", Version: "1.0.0"}, - {Name: "rsc.io/binaryregexp", Version: "0.2.0"}, - {Name: "rsc.io/quote/v3", Version: "3.1.0"}, - {Name: "rsc.io/sampler", Version: "1.3.0"}, - {Name: "sigs.k8s.io/structured-merge-diff", Version: "1.0.1-0.20191108220359-b1b620dd3f06"}, - {Name: "sigs.k8s.io/yaml", Version: "1.1.0"}, + {Name: "cloud.google.com/go", Version: "v0.65.0"}, + {Name: "cloud.google.com/go/bigquery", Version: "v1.8.0"}, + {Name: "cloud.google.com/go/datastore", Version: "v1.1.0"}, + {Name: "cloud.google.com/go/pubsub", Version: "v1.3.1"}, + {Name: "cloud.google.com/go/storage", Version: "v1.10.0"}, + {Name: "dmitri.shuralyov.com/gpu/mtl", Version: "v0.0.0-20190408044501-666a987793e9"}, + {Name: "github.com/Azure/azure-sdk-for-go", Version: "v38.0.0+incompatible"}, + {Name: "github.com/Azure/go-ansiterm", Version: "v0.0.0-20170929234023-d6e3b3328b78"}, + {Name: "github.com/Azure/go-autorest/autorest", Version: "v0.9.3"}, + {Name: "github.com/Azure/go-autorest/autorest/adal", Version: "v0.8.1"}, + {Name: "github.com/Azure/go-autorest/autorest/date", Version: "v0.2.0"}, + {Name: "github.com/Azure/go-autorest/autorest/mocks", Version: "v0.3.0"}, + {Name: "github.com/Azure/go-autorest/autorest/to", Version: "v0.3.0"}, + {Name: "github.com/Azure/go-autorest/autorest/validation", Version: "v0.1.0"}, + {Name: "github.com/Azure/go-autorest/logger", Version: "v0.1.0"}, + {Name: "github.com/Azure/go-autorest/tracing", Version: "v0.5.0"}, + {Name: "github.com/BurntSushi/toml", Version: "v0.3.1"}, + {Name: "github.com/BurntSushi/xgb", Version: "v0.0.0-20160522181843-27f122750802"}, + {Name: "github.com/GoogleCloudPlatform/docker-credential-gcr", Version: "v1.5.0"}, + {Name: "github.com/GoogleCloudPlatform/k8s-cloud-provider", Version: "v0.0.0-20190822182118-27a4ced34534"}, + {Name: "github.com/Microsoft/go-winio", Version: "v0.4.15-0.20190919025122-fc70bd9a86b5"}, + {Name: "github.com/Microsoft/hcsshim", Version: "v0.8.6"}, + {Name: "github.com/NYTimes/gziphandler", Version: "v0.0.0-20170623195520-56545f4a5d46"}, + {Name: "github.com/OneOfOne/xxhash", Version: "v1.2.7"}, + {Name: "github.com/PuerkitoBio/purell", Version: "v1.1.1"}, + {Name: "github.com/PuerkitoBio/urlesc", Version: "v0.0.0-20170810143723-de5bf2ad4578"}, + {Name: "github.com/VividCortex/ewma", Version: "v1.1.1"}, + {Name: "github.com/alcortesm/tgz", Version: "v0.0.0-20161220082320-9c5fe88206d7"}, + {Name: "github.com/alecthomas/template", Version: "v0.0.0-20160405071501-a0175ee3bccc"}, + {Name: "github.com/alecthomas/units", Version: "v0.0.0-20151022065526-2efee857e7cf"}, + {Name: "github.com/alicebob/gopher-json", Version: "v0.0.0-20200520072559-a9ecdc9d1d3a"}, + {Name: "github.com/alicebob/miniredis/v2", Version: "v2.14.1"}, + {Name: "github.com/anmitsu/go-shlex", Version: "v0.0.0-20161002113705-648efa622239"}, + {Name: "github.com/aquasecurity/bolt-fixtures", Version: "v0.0.0-20200903104109-d34e7f983986"}, + {Name: "github.com/aquasecurity/fanal", Version: "v0.0.0-20210119051230-28c249da7cfd"}, + {Name: "github.com/aquasecurity/go-dep-parser", Version: "v0.0.0-20201028043324-889d4a92b8e0"}, + {Name: "github.com/aquasecurity/go-gem-version", Version: "v0.0.0-20201115065557-8eed6fe000ce"}, + {Name: "github.com/aquasecurity/go-npm-version", Version: "v0.0.0-20201110091526-0b796d180798"}, + {Name: "github.com/aquasecurity/go-pep440-version", Version: "v0.0.0-20210121094942-22b2f8951d46"}, + {Name: "github.com/aquasecurity/go-version", Version: "v0.0.0-20210121072130-637058cfe492"}, + {Name: "github.com/aquasecurity/testdocker", Version: "v0.0.0-20210106133225-0b17fe083674"}, + {Name: "github.com/aquasecurity/trivy", Version: "v0.16.0"}, + {Name: "github.com/aquasecurity/trivy-db", Version: "v0.0.0-20210105160501-c5bf4e153277"}, + {Name: "github.com/aquasecurity/vuln-list-update", Version: "v0.0.0-20191016075347-3d158c2bf9a2"}, + {Name: "github.com/araddon/dateparse", Version: "v0.0.0-20190426192744-0d74ffceef83"}, + {Name: "github.com/armon/consul-api", Version: "v0.0.0-20180202201655-eb2c6b5be1b6"}, + {Name: "github.com/armon/go-socks5", Version: "v0.0.0-20160902184237-e75332964ef5"}, + {Name: "github.com/aws/aws-sdk-go", Version: "v1.27.1"}, + {Name: "github.com/beorn7/perks", Version: "v1.0.0"}, + {Name: "github.com/bgentry/speakeasy", Version: "v0.1.0"}, + {Name: "github.com/blang/semver", Version: "v3.5.0+incompatible"}, + {Name: "github.com/briandowns/spinner", Version: "v1.12.0"}, + {Name: "github.com/caarlos0/env/v6", Version: "v6.0.0"}, + {Name: "github.com/cenkalti/backoff", Version: "v2.2.1+incompatible"}, + {Name: "github.com/census-instrumentation/opencensus-proto", Version: "v0.2.1"}, + {Name: "github.com/cespare/xxhash/v2", Version: "v2.1.1"}, + {Name: "github.com/cheggaaa/pb/v3", Version: "v3.0.3"}, + {Name: "github.com/chzyer/logex", Version: "v1.1.10"}, + {Name: "github.com/chzyer/readline", Version: "v0.0.0-20180603132655-2972be24d48e"}, + {Name: "github.com/chzyer/test", Version: "v0.0.0-20180213035817-a1ea475d72b1"}, + {Name: "github.com/client9/misspell", Version: "v0.3.4"}, + {Name: "github.com/cncf/udpa/go", Version: "v0.0.0-20191209042840-269d4d468f6f"}, + {Name: "github.com/cockroachdb/datadriven", Version: "v0.0.0-20190809214429-80d97fb3cbaa"}, + {Name: "github.com/containerd/containerd", Version: "v1.3.3"}, + {Name: "github.com/containerd/continuity", Version: "v0.0.0-20190426062206-aaeac12a7ffc"}, + {Name: "github.com/coreos/etcd", Version: "v3.3.10+incompatible"}, + {Name: "github.com/coreos/go-etcd", Version: "v2.0.0+incompatible"}, + {Name: "github.com/coreos/go-oidc", Version: "v2.1.0+incompatible"}, + {Name: "github.com/coreos/go-semver", Version: "v0.3.0"}, + {Name: "github.com/coreos/go-systemd", Version: "v0.0.0-20190321100706-95778dfbb74e"}, + {Name: "github.com/coreos/pkg", Version: "v0.0.0-20180108230652-97fdf19511ea"}, + {Name: "github.com/cpuguy83/go-md2man", Version: "v1.0.10"}, + {Name: "github.com/cpuguy83/go-md2man/v2", Version: "v2.0.0"}, + {Name: "github.com/creack/pty", Version: "v1.1.9"}, + {Name: "github.com/davecgh/go-spew", Version: "v1.1.1"}, + {Name: "github.com/deckarep/golang-set", Version: "v1.7.1"}, + {Name: "github.com/dgrijalva/jwt-go", Version: "v3.2.0+incompatible"}, + {Name: "github.com/dgryski/go-rendezvous", Version: "v0.0.0-20200823014737-9f7001d12a5f"}, + {Name: "github.com/dnaeon/go-vcr", Version: "v1.0.1"}, + {Name: "github.com/docker/cli", Version: "v0.0.0-20191017083524-a8ff7f821017"}, + {Name: "github.com/docker/distribution", Version: "v2.7.1+incompatible"}, + {Name: "github.com/docker/docker", Version: "v1.4.2-0.20190924003213-a8608b5b67c7"}, + {Name: "github.com/docker/docker-credential-helpers", Version: "v0.6.3"}, + {Name: "github.com/docker/go-connections", Version: "v0.4.0"}, + {Name: "github.com/docker/go-units", Version: "v0.4.0"}, + {Name: "github.com/docker/spdystream", Version: "v0.0.0-20160310174837-449fdfce4d96"}, + {Name: "github.com/dustin/go-humanize", Version: "v1.0.0"}, + {Name: "github.com/elazarl/goproxy", Version: "v0.0.0-20200809112317-0581fc3aee2d"}, + {Name: "github.com/elazarl/goproxy/ext", Version: "v0.0.0-20200809112317-0581fc3aee2d"}, + {Name: "github.com/emicklei/go-restful", Version: "v2.9.5+incompatible"}, + {Name: "github.com/emirpasic/gods", Version: "v1.12.0"}, + {Name: "github.com/envoyproxy/go-control-plane", Version: "v0.9.4"}, + {Name: "github.com/envoyproxy/protoc-gen-validate", Version: "v0.1.0"}, + {Name: "github.com/evanphx/json-patch", Version: "v4.2.0+incompatible"}, + {Name: "github.com/fatih/color", Version: "v1.10.0"}, + {Name: "github.com/flynn/go-shlex", Version: "v0.0.0-20150515145356-3f9db97f8568"}, + {Name: "github.com/fsnotify/fsnotify", Version: "v1.4.9"}, + {Name: "github.com/ghodss/yaml", Version: "v1.0.0"}, + {Name: "github.com/gin-contrib/sse", Version: "v0.1.0"}, + {Name: "github.com/gin-gonic/gin", Version: "v1.5.0"}, + {Name: "github.com/gliderlabs/ssh", Version: "v0.2.2"}, + {Name: "github.com/go-git/gcfg", Version: "v1.5.0"}, + {Name: "github.com/go-git/go-billy/v5", Version: "v5.0.0"}, + {Name: "github.com/go-git/go-git-fixtures/v4", Version: "v4.0.1"}, + {Name: "github.com/go-git/go-git/v5", Version: "v5.0.0"}, + {Name: "github.com/go-gl/glfw", Version: "v0.0.0-20190409004039-e6da0acd62b1"}, + {Name: "github.com/go-gl/glfw/v3.3/glfw", Version: "v0.0.0-20200222043503-6f7a984d4dc4"}, + {Name: "github.com/go-kit/kit", Version: "v0.8.0"}, + {Name: "github.com/go-logfmt/logfmt", Version: "v0.3.0"}, + {Name: "github.com/go-logr/logr", Version: "v0.1.0"}, + {Name: "github.com/go-openapi/jsonpointer", Version: "v0.19.3"}, + {Name: "github.com/go-openapi/jsonreference", Version: "v0.19.3"}, + {Name: "github.com/go-openapi/spec", Version: "v0.19.3"}, + {Name: "github.com/go-openapi/swag", Version: "v0.19.5"}, + {Name: "github.com/go-playground/locales", Version: "v0.13.0"}, + {Name: "github.com/go-playground/universal-translator", Version: "v0.17.0"}, + {Name: "github.com/go-redis/redis", Version: "v6.15.7+incompatible"}, + {Name: "github.com/go-redis/redis/v8", Version: "v8.4.0"}, + {Name: "github.com/go-restruct/restruct", Version: "v0.0.0-20191227155143-5734170a48a1"}, + {Name: "github.com/go-sql-driver/mysql", Version: "v1.5.0"}, + {Name: "github.com/go-stack/stack", Version: "v1.8.0"}, + {Name: "github.com/gobwas/glob", Version: "v0.2.3"}, + {Name: "github.com/goccy/go-yaml", Version: "v1.8.2"}, + {Name: "github.com/gogo/protobuf", Version: "v1.3.1"}, + {Name: "github.com/golang/glog", Version: "v0.0.0-20160126235308-23def4e6c14b"}, + {Name: "github.com/golang/groupcache", Version: "v0.0.0-20200121045136-8c9f03a8e57e"}, + {Name: "github.com/golang/mock", Version: "v1.4.4"}, + {Name: "github.com/golang/protobuf", Version: "v1.4.2"}, + {Name: "github.com/google/btree", Version: "v1.0.0"}, + {Name: "github.com/google/go-cmp", Version: "v0.5.3"}, + {Name: "github.com/google/go-containerregistry", Version: "v0.0.0-20200331213917-3d03ed9b1ca2"}, + {Name: "github.com/google/go-github/v28", Version: "v28.1.1"}, + {Name: "github.com/google/go-querystring", Version: "v1.0.0"}, + {Name: "github.com/google/gofuzz", Version: "v1.0.0"}, + {Name: "github.com/google/martian", Version: "v2.1.0+incompatible"}, + {Name: "github.com/google/martian/v3", Version: "v3.0.0"}, + {Name: "github.com/google/pprof", Version: "v0.0.0-20200708004538-1a94d8640e99"}, + {Name: "github.com/google/renameio", Version: "v0.1.0"}, + {Name: "github.com/google/subcommands", Version: "v1.0.1"}, + {Name: "github.com/google/uuid", Version: "v1.1.1"}, + {Name: "github.com/google/wire", Version: "v0.3.0"}, + {Name: "github.com/googleapis/gax-go/v2", Version: "v2.0.5"}, + {Name: "github.com/googleapis/gnostic", Version: "v0.2.2"}, + {Name: "github.com/gophercloud/gophercloud", Version: "v0.1.0"}, + {Name: "github.com/gopherjs/gopherjs", Version: "v0.0.0-20200217142428-fce0ec30dd00"}, + {Name: "github.com/gorilla/context", Version: "v1.1.1"}, + {Name: "github.com/gorilla/mux", Version: "v1.7.4"}, + {Name: "github.com/gorilla/websocket", Version: "v1.4.0"}, + {Name: "github.com/gregjones/httpcache", Version: "v0.0.0-20180305231024-9cad4c3443a7"}, + {Name: "github.com/grpc-ecosystem/go-grpc-middleware", Version: "v1.0.1-0.20190118093823-f849b5445de4"}, + {Name: "github.com/grpc-ecosystem/go-grpc-prometheus", Version: "v1.2.0"}, + {Name: "github.com/grpc-ecosystem/grpc-gateway", Version: "v1.9.5"}, + {Name: "github.com/hashicorp/errwrap", Version: "v1.0.0"}, + {Name: "github.com/hashicorp/go-multierror", Version: "v1.1.0"}, + {Name: "github.com/hashicorp/go-version", Version: "v1.2.1"}, + {Name: "github.com/hashicorp/golang-lru", Version: "v0.5.3"}, + {Name: "github.com/hashicorp/hcl", Version: "v1.0.0"}, + {Name: "github.com/hpcloud/tail", Version: "v1.0.0"}, + {Name: "github.com/ianlancetaylor/demangle", Version: "v0.0.0-20181102032728-5e5cf60278f6"}, + {Name: "github.com/imdario/mergo", Version: "v0.3.5"}, + {Name: "github.com/inconshreveable/mousetrap", Version: "v1.0.0"}, + {Name: "github.com/jbenet/go-context", Version: "v0.0.0-20150711004518-d14ea06fba99"}, + {Name: "github.com/jessevdk/go-flags", Version: "v1.4.0"}, + {Name: "github.com/jmespath/go-jmespath", Version: "v0.0.0-20180206201540-c2b33e8439af"}, + {Name: "github.com/joefitzgerald/rainbow-reporter", Version: "v0.1.0"}, + {Name: "github.com/jonboulle/clockwork", Version: "v0.1.0"}, + {Name: "github.com/json-iterator/go", Version: "v1.1.8"}, + {Name: "github.com/jstemmer/go-junit-report", Version: "v0.9.1"}, + {Name: "github.com/jtolds/gls", Version: "v4.20.0+incompatible"}, + {Name: "github.com/julienschmidt/httprouter", Version: "v1.2.0"}, + {Name: "github.com/kevinburke/ssh_config", Version: "v0.0.0-20190725054713-01f96b0aa0cd"}, + {Name: "github.com/kisielk/errcheck", Version: "v1.2.0"}, + {Name: "github.com/kisielk/gotool", Version: "v1.0.0"}, + {Name: "github.com/knqyf263/go-apk-version", Version: "v0.0.0-20200609155635-041fdbb8563f"}, + {Name: "github.com/knqyf263/go-deb-version", Version: "v0.0.0-20190517075300-09fca494f03d"}, + {Name: "github.com/knqyf263/go-rpm-version", Version: "v0.0.0-20170716094938-74609b86c936"}, + {Name: "github.com/knqyf263/go-rpmdb", Version: "v0.0.0-20201215100354-a9e3110d8ee1"}, + {Name: "github.com/knqyf263/nested", Version: "v0.0.1"}, + {Name: "github.com/konsorten/go-windows-terminal-sequences", Version: "v1.0.2"}, + {Name: "github.com/kr/logfmt", Version: "v0.0.0-20140226030751-b84e30acd515"}, + {Name: "github.com/kr/pretty", Version: "v0.1.0"}, + {Name: "github.com/kr/pty", Version: "v1.1.5"}, + {Name: "github.com/kr/text", Version: "v0.2.0"}, + {Name: "github.com/kylelemons/godebug", Version: "v1.1.0"}, + {Name: "github.com/leodido/go-urn", Version: "v1.2.0"}, + {Name: "github.com/magiconair/properties", Version: "v1.8.0"}, + {Name: "github.com/mailru/easyjson", Version: "v0.7.0"}, + {Name: "github.com/mattn/go-colorable", Version: "v0.1.8"}, + {Name: "github.com/mattn/go-isatty", Version: "v0.0.12"}, + {Name: "github.com/mattn/go-jsonpointer", Version: "v0.0.0-20180225143300-37667080efed"}, + {Name: "github.com/mattn/go-runewidth", Version: "v0.0.9"}, + {Name: "github.com/matttproud/golang_protobuf_extensions", Version: "v1.0.1"}, + {Name: "github.com/maxbrunsfeld/counterfeiter/v6", Version: "v6.2.2"}, + {Name: "github.com/mitchellh/go-homedir", Version: "v1.1.0"}, + {Name: "github.com/mitchellh/mapstructure", Version: "v1.1.2"}, + {Name: "github.com/modern-go/concurrent", Version: "v0.0.0-20180306012644-bacd9c7ef1dd"}, + {Name: "github.com/modern-go/reflect2", Version: "v1.0.1"}, + {Name: "github.com/morikuni/aec", Version: "v1.0.0"}, + {Name: "github.com/munnerz/goautoneg", Version: "v0.0.0-20191010083416-a7dc8b61c822"}, + {Name: "github.com/mwitkow/go-conntrack", Version: "v0.0.0-20161129095857-cc309e4a2223"}, + {Name: "github.com/mxk/go-flowrate", Version: "v0.0.0-20140419014527-cca7078d478f"}, + {Name: "github.com/niemeyer/pretty", Version: "v0.0.0-20200227124842-a10e7caefd8e"}, + {Name: "github.com/nxadm/tail", Version: "v1.4.4"}, + {Name: "github.com/olekukonko/tablewriter", Version: "v0.0.2-0.20190607075207-195002e6e56a"}, + {Name: "github.com/onsi/ginkgo", Version: "v1.14.2"}, + {Name: "github.com/onsi/gomega", Version: "v1.10.3"}, + {Name: "github.com/open-policy-agent/opa", Version: "v0.21.1"}, + {Name: "github.com/opencontainers/go-digest", Version: "v1.0.0-rc1"}, + {Name: "github.com/opencontainers/image-spec", Version: "v1.0.2-0.20190823105129-775207bd45b6"}, + {Name: "github.com/opencontainers/runc", Version: "v0.1.1"}, + {Name: "github.com/parnurzeal/gorequest", Version: "v0.2.16"}, + {Name: "github.com/pelletier/go-toml", Version: "v1.2.0"}, + {Name: "github.com/peterbourgon/diskv", Version: "v2.0.1+incompatible"}, + {Name: "github.com/peterh/liner", Version: "v0.0.0-20170211195444-bf27d3ba8e1d"}, + {Name: "github.com/pkg/errors", Version: "v0.9.1"}, + {Name: "github.com/pmezard/go-difflib", Version: "v1.0.0"}, + {Name: "github.com/pquerna/cachecontrol", Version: "v0.0.0-20171018203845-0dec1b30a021"}, + {Name: "github.com/prometheus/client_golang", Version: "v1.0.0"}, + {Name: "github.com/prometheus/client_model", Version: "v0.0.0-20190812154241-14fe0d1b01d4"}, + {Name: "github.com/prometheus/common", Version: "v0.4.1"}, + {Name: "github.com/prometheus/procfs", Version: "v0.0.2"}, + {Name: "github.com/rcrowley/go-metrics", Version: "v0.0.0-20181016184325-3113b8401b8a"}, + {Name: "github.com/remyoudompheng/bigfft", Version: "v0.0.0-20170806203942-52369c62f446"}, + {Name: "github.com/rogpeppe/fastuuid", Version: "v0.0.0-20150106093220-6724a57986af"}, + {Name: "github.com/rogpeppe/go-charset", Version: "v0.0.0-20180617210344-2471d30d28b4"}, + {Name: "github.com/rogpeppe/go-internal", Version: "v1.3.0"}, + {Name: "github.com/rubiojr/go-vhd", Version: "v0.0.0-20160810183302-0bfd3b39853c"}, + {Name: "github.com/russross/blackfriday", Version: "v1.5.2"}, + {Name: "github.com/russross/blackfriday/v2", Version: "v2.0.1"}, + {Name: "github.com/saracen/walker", Version: "v0.0.0-20191201085201-324a081bae7e"}, + {Name: "github.com/satori/go.uuid", Version: "v1.2.0"}, + {Name: "github.com/sclevine/spec", Version: "v1.2.0"}, + {Name: "github.com/sergi/go-diff", Version: "v1.1.0"}, + {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "v1.0.0"}, + {Name: "github.com/simplereach/timeutils", Version: "v1.2.0"}, + {Name: "github.com/sirupsen/logrus", Version: "v1.5.0"}, + {Name: "github.com/smartystreets/assertions", Version: "v1.2.0"}, + {Name: "github.com/smartystreets/goconvey", Version: "v1.6.4"}, + {Name: "github.com/soheilhy/cmux", Version: "v0.1.4"}, + {Name: "github.com/sosedoff/gitkit", Version: "v0.2.0"}, + {Name: "github.com/spf13/afero", Version: "v1.2.2"}, + {Name: "github.com/spf13/cast", Version: "v1.3.0"}, + {Name: "github.com/spf13/cobra", Version: "v0.0.5"}, + {Name: "github.com/spf13/jwalterweatherman", Version: "v1.0.0"}, + {Name: "github.com/spf13/pflag", Version: "v1.0.5"}, + {Name: "github.com/spf13/viper", Version: "v1.3.2"}, + {Name: "github.com/stretchr/objx", Version: "v0.3.0"}, + {Name: "github.com/stretchr/testify", Version: "v1.6.1"}, + {Name: "github.com/testcontainers/testcontainers-go", Version: "v0.3.1"}, + {Name: "github.com/tmc/grpc-websocket-proxy", Version: "v0.0.0-20170815181823-89b8d40f7ca8"}, + {Name: "github.com/twitchtv/twirp", Version: "v5.10.1+incompatible"}, + {Name: "github.com/ugorji/go", Version: "v1.1.7"}, + {Name: "github.com/ugorji/go/codec", Version: "v1.1.7"}, + {Name: "github.com/urfave/cli", Version: "v1.22.5"}, + {Name: "github.com/urfave/cli/v2", Version: "v2.3.0"}, + {Name: "github.com/vdemeester/k8s-pkg-credentialprovider", Version: "v1.17.4"}, + {Name: "github.com/vmware/govmomi", Version: "v0.20.3"}, + {Name: "github.com/xanzy/ssh-agent", Version: "v0.2.1"}, + {Name: "github.com/xiang90/probing", Version: "v0.0.0-20190116061207-43a291ad63a2"}, + {Name: "github.com/xordataexchange/crypt", Version: "v0.0.3-0.20170626215501-b2862e3d0a77"}, + {Name: "github.com/yashtewari/glob-intersection", Version: "v0.0.0-20180916065949-5c77d914dd0b"}, + {Name: "github.com/yuin/goldmark", Version: "v1.1.32"}, + {Name: "github.com/yuin/gopher-lua", Version: "v0.0.0-20191220021717-ab39c6098bdb"}, + {Name: "go.etcd.io/bbolt", Version: "v1.3.5"}, + {Name: "go.etcd.io/etcd", Version: "v0.0.0-20191023171146-3cf2f69b5738"}, + {Name: "go.opencensus.io", Version: "v0.22.4"}, + {Name: "go.opentelemetry.io/otel", Version: "v0.14.0"}, + {Name: "go.uber.org/atomic", Version: "v1.5.1"}, + {Name: "go.uber.org/multierr", Version: "v1.4.0"}, + {Name: "go.uber.org/tools", Version: "v0.0.0-20190618225709-2cfd321de3ee"}, + {Name: "go.uber.org/zap", Version: "v1.13.0"}, + {Name: "golang.org/x/crypto", Version: "v0.0.0-20201002170205-7f63de1d35b0"}, + {Name: "golang.org/x/exp", Version: "v0.0.0-20200224162631-6cc2880d07d6"}, + {Name: "golang.org/x/image", Version: "v0.0.0-20190802002840-cff245a6509b"}, + {Name: "golang.org/x/lint", Version: "v0.0.0-20200302205851-738671d3881b"}, + {Name: "golang.org/x/mobile", Version: "v0.0.0-20190719004257-d2bd2a29d028"}, + {Name: "golang.org/x/mod", Version: "v0.3.0"}, + {Name: "golang.org/x/net", Version: "v0.0.0-20201006153459-a7d1128ccaa0"}, + {Name: "golang.org/x/oauth2", Version: "v0.0.0-20201208152858-08078c50e5b5"}, + {Name: "golang.org/x/sync", Version: "v0.0.0-20200625203802-6e8e738ad208"}, + {Name: "golang.org/x/sys", Version: "v0.0.0-20201006155630-ac719f4daadf"}, + {Name: "golang.org/x/text", Version: "v0.3.3"}, + {Name: "golang.org/x/time", Version: "v0.0.0-20191024005414-555d28b269f0"}, + {Name: "golang.org/x/tools", Version: "v0.0.0-20200825202427-b303f430e36d"}, + {Name: "golang.org/x/xerrors", Version: "v0.0.0-20200804184101-5ec99f83aff1"}, + {Name: "gonum.org/v1/gonum", Version: "v0.0.0-20190331200053-3d26580ed485"}, + {Name: "gonum.org/v1/netlib", Version: "v0.0.0-20190331212654-76723241ea4e"}, + {Name: "google.golang.org/api", Version: "v0.30.0"}, + {Name: "google.golang.org/appengine", Version: "v1.6.6"}, + {Name: "google.golang.org/genproto", Version: "v0.0.0-20200825200019-8632dd797987"}, + {Name: "google.golang.org/grpc", Version: "v1.31.0"}, + {Name: "google.golang.org/protobuf", Version: "v1.25.0"}, + {Name: "gopkg.in/alecthomas/kingpin.v2", Version: "v2.2.6"}, + {Name: "gopkg.in/check.v1", Version: "v1.0.0-20200902074654-038fdea0a05b"}, + {Name: "gopkg.in/cheggaaa/pb.v1", Version: "v1.0.28"}, + {Name: "gopkg.in/errgo.v2", Version: "v2.1.0"}, + {Name: "gopkg.in/fsnotify.v1", Version: "v1.4.7"}, + {Name: "gopkg.in/gcfg.v1", Version: "v1.2.0"}, + {Name: "gopkg.in/go-playground/assert.v1", Version: "v1.2.1"}, + {Name: "gopkg.in/go-playground/validator.v9", Version: "v9.31.0"}, + {Name: "gopkg.in/inf.v0", Version: "v0.9.1"}, + {Name: "gopkg.in/mgo.v2", Version: "v2.0.0-20180705113604-9856a29383ce"}, + {Name: "gopkg.in/natefinch/lumberjack.v2", Version: "v2.0.0"}, + {Name: "gopkg.in/resty.v1", Version: "v1.12.0"}, + {Name: "gopkg.in/square/go-jose.v2", Version: "v2.2.2"}, + {Name: "gopkg.in/tomb.v1", Version: "v1.0.0-20141024135613-dd632973f1e7"}, + {Name: "gopkg.in/warnings.v0", Version: "v0.1.2"}, + {Name: "gopkg.in/yaml.v2", Version: "v2.4.0"}, + {Name: "gopkg.in/yaml.v3", Version: "v3.0.0-20200615113413-eeeca48fe776"}, + {Name: "gotest.tools", Version: "v2.2.0+incompatible"}, + {Name: "honnef.co/go/tools", Version: "v0.0.1-2020.1.4"}, + {Name: "k8s.io/api", Version: "v0.17.4"}, + {Name: "k8s.io/apimachinery", Version: "v0.17.4"}, + {Name: "k8s.io/apiserver", Version: "v0.17.4"}, + {Name: "k8s.io/client-go", Version: "v0.17.4"}, + {Name: "k8s.io/cloud-provider", Version: "v0.17.4"}, + {Name: "k8s.io/code-generator", Version: "v0.17.2"}, + {Name: "k8s.io/component-base", Version: "v0.17.4"}, + {Name: "k8s.io/csi-translation-lib", Version: "v0.17.4"}, + {Name: "k8s.io/gengo", Version: "v0.0.0-20190822140433-26a664648505"}, + {Name: "k8s.io/klog", Version: "v1.0.0"}, + {Name: "k8s.io/klog/v2", Version: "v2.0.0"}, + {Name: "k8s.io/kube-openapi", Version: "v0.0.0-20191107075043-30be4d16710a"}, + {Name: "k8s.io/legacy-cloud-providers", Version: "v0.17.4"}, + {Name: "k8s.io/utils", Version: "v0.0.0-20201110183641-67b214c5f920"}, + {Name: "modernc.org/cc", Version: "v1.0.0"}, + {Name: "modernc.org/golex", Version: "v1.0.0"}, + {Name: "modernc.org/mathutil", Version: "v1.0.0"}, + {Name: "modernc.org/strutil", Version: "v1.0.0"}, + {Name: "modernc.org/xc", Version: "v1.0.0"}, + {Name: "moul.io/http2curl", Version: "v1.0.0"}, + {Name: "rsc.io/binaryregexp", Version: "v0.2.0"}, + {Name: "rsc.io/quote/v3", Version: "v3.1.0"}, + {Name: "rsc.io/sampler", Version: "v1.3.0"}, + {Name: "sigs.k8s.io/structured-merge-diff", Version: "v1.0.1-0.20191108220359-b1b620dd3f06"}, + {Name: "sigs.k8s.io/yaml", Version: "v1.1.0"}, } ) diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index 73ebe6b21407..8fc20b236b51 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -163,7 +163,7 @@ func evaluateVariable(s string, props map[string]string, seenProps []string) str } s = strings.ReplaceAll(s, m[0], newValue) } - return s + return strings.TrimSpace(s) } func printLoopedPropertiesStack(env string, usedProps []string) { diff --git a/pkg/dependency/parser/java/pom/parse.go b/pkg/dependency/parser/java/pom/parse.go index 542abcac3c8a..60a83ba9d81b 100644 --- a/pkg/dependency/parser/java/pom/parse.go +++ b/pkg/dependency/parser/java/pom/parse.go @@ -96,7 +96,7 @@ func NewParser(filePath string, opts ...option) *Parser { } func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) { - content, err := parsePom(r) + content, err := parsePom(r, true) if err != nil { return nil, nil, xerrors.Errorf("failed to parse POM: %w", err) } @@ -107,7 +107,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc } // Analyze root POM - result, err := p.analyze(root, analysisOptions{lineNumber: true}) + result, err := p.analyze(root, analysisOptions{}) if err != nil { return nil, nil, xerrors.Errorf("analyze error (%s): %w", p.rootPath, err) } @@ -330,53 +330,27 @@ type analysisResult struct { type analysisOptions struct { exclusions map[string]struct{} depManagement []pomDependency // from the root POM - lineNumber bool // Save line numbers } func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) { - if pom == nil || pom.content == nil { + if pom.nil() { return analysisResult{}, nil } - // Update remoteRepositories pomReleaseRemoteRepos, pomSnapshotRemoteRepos := pom.repositories(p.servers) p.releaseRemoteRepos = lo.Uniq(append(pomReleaseRemoteRepos, p.releaseRemoteRepos...)) p.snapshotRemoteRepos = lo.Uniq(append(pomSnapshotRemoteRepos, p.snapshotRemoteRepos...)) - // We need to forward dependencyManagements from current and root pom to Parent, - // to use them for dependencies in parent. - // For better understanding see the following tests: - // - `dependency from parent uses version from child pom depManagement` - // - `dependency from parent uses version from root pom depManagement` - // - // depManagements from root pom has higher priority than depManagements from current pom. - depManagementForParent := lo.UniqBy(append(opts.depManagement, pom.content.DependencyManagement.Dependencies.Dependency...), - func(dep pomDependency) string { - return dep.Name() - }) - - // Parent - parent, err := p.parseParent(pom.filePath, pom.content.Parent, depManagementForParent) - if err != nil { - return analysisResult{}, xerrors.Errorf("parent error: %w", err) + // Resolve parent POM + if err := p.resolveParent(pom); err != nil { + return analysisResult{}, xerrors.Errorf("pom resolve error: %w", err) } - // Inherit values/properties from parent - pom.inherit(parent) - - // Generate properties + // Resolve dependencies props := pom.properties() - - // dependencyManagements have the next priority: - // 1. Managed dependencies from this POM - // 2. Managed dependencies from parent of this POM - depManagement := p.mergeDependencyManagements(pom.content.DependencyManagement.Dependencies.Dependency, - parent.dependencyManagement) - - // Merge dependencies. Child dependencies must be preferred than parent dependencies. - // Parents don't have to resolve dependencies. + depManagement := pom.content.DependencyManagement.Dependencies.Dependency deps := p.parseDependencies(pom.content.Dependencies.Dependency, props, depManagement, opts) - deps = p.mergeDependencies(parent.dependencies, deps, opts.exclusions) + deps = p.filterDependencies(deps, opts.exclusions) return analysisResult{ filePath: pom.filePath, @@ -388,6 +362,39 @@ func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) }, nil } +// resolveParent resolves its parent POMs and inherits properties, dependencies, and dependencyManagement. +func (p *Parser) resolveParent(pom *pom) error { + if pom.nil() { + return nil + } + + // Parse parent POM + parent, err := p.parseParent(pom.filePath, pom.content.Parent) + if err != nil { + return xerrors.Errorf("parent error: %w", err) + } + + // Inherit values/properties from parent + pom.inherit(parent) + + // Merge properties + pom.content.Properties = p.mergeProperties(pom.content.Properties, parent.content.Properties) + + // Merge dependencyManagement with the following priority: + // 1. Managed dependencies from this POM + // 2. Managed dependencies from parent of this POM + pom.content.DependencyManagement.Dependencies.Dependency = p.mergeDependencyManagements( + pom.content.DependencyManagement.Dependencies.Dependency, + parent.content.DependencyManagement.Dependencies.Dependency) + + // Merge dependencies + pom.content.Dependencies.Dependency = p.mergeDependencies( + pom.content.Dependencies.Dependency, + parent.content.Dependencies.Dependency) + + return nil +} + func (p *Parser) mergeDependencyManagements(depManagements ...[]pomDependency) []pomDependency { uniq := make(map[string]struct{}) var depManagement []pomDependency @@ -463,22 +470,20 @@ func (p *Parser) resolveDepManagement(props map[string]string, depManagement []p return newDepManagement } -func (p *Parser) mergeDependencies(parent, child []artifact, exclusions map[string]struct{}) []artifact { - var deps []artifact - unique := make(map[string]struct{}) +func (p *Parser) mergeProperties(child, parent properties) properties { + return lo.Assign(parent, child) +} - for _, d := range append(child, parent...) { - if excludeDep(exclusions, d) { - continue - } - if _, ok := unique[d.Name()]; ok { - continue - } - unique[d.Name()] = struct{}{} - deps = append(deps, d) - } +func (p *Parser) mergeDependencies(child, parent []pomDependency) []pomDependency { + return lo.UniqBy(append(child, parent...), func(d pomDependency) string { + return d.Name() + }) +} - return deps +func (p *Parser) filterDependencies(artifacts []artifact, exclusions map[string]struct{}) []artifact { + return lo.Filter(artifacts, func(art artifact, _ int) bool { + return !excludeDep(exclusions, art) + }) } func excludeDep(exclusions map[string]struct{}, art artifact) bool { @@ -497,38 +502,29 @@ func excludeDep(exclusions map[string]struct{}, art artifact) bool { return false } -func (p *Parser) parseParent(currentPath string, parent pomParent, rootDepManagement []pomDependency) (analysisResult, error) { +func (p *Parser) parseParent(currentPath string, parent pomParent) (*pom, error) { // Pass nil properties so that variables in are not evaluated. target := newArtifact(parent.GroupId, parent.ArtifactId, parent.Version, nil, nil) // if version is property (e.g. ${revision}) - we still need to parse this pom if target.IsEmpty() && !isProperty(parent.Version) { - return analysisResult{}, nil + return &pom{content: &pomXML{}}, nil } logger := p.logger.With("artifact", target.String()) logger.Debug("Start parent") defer logger.Debug("Exit parent") - // If the artifact is found in cache, it is returned. - if result := p.cache.get(target); result != nil { - return *result, nil - } - parentPOM, err := p.retrieveParent(currentPath, parent.RelativePath, target) if err != nil { logger.Debug("Parent POM not found", log.Err(err)) + return &pom{content: &pomXML{}}, nil } - result, err := p.analyze(parentPOM, analysisOptions{ - depManagement: rootDepManagement, - }) - if err != nil { - return analysisResult{}, xerrors.Errorf("analyze error: %w", err) + if err = p.resolveParent(parentPOM); err != nil { + return nil, xerrors.Errorf("parent pom resolve error: %w", err) } - p.cache.put(target, result) - - return result, nil + return parentPOM, nil } func (p *Parser) retrieveParent(currentPath, relativePath string, target artifact) (*pom, error) { @@ -565,7 +561,7 @@ func (p *Parser) retrieveParent(currentPath, relativePath string, target artifac } func (p *Parser) tryRelativePath(parentArtifact artifact, currentPath, relativePath string) (*pom, error) { - pom, err := p.openRelativePom(currentPath, relativePath) + parsedPOM, err := p.openRelativePom(currentPath, relativePath) if err != nil { return nil, err } @@ -576,19 +572,18 @@ func (p *Parser) tryRelativePath(parentArtifact artifact, currentPath, relativeP // But GroupID can be inherited from parent (`p.analyze` function is required to get the GroupID). // Version can contain a property (`p.analyze` function is required to get the GroupID). // So we can only match ArtifactID's. - if pom.artifact().ArtifactID != parentArtifact.ArtifactID { + if parsedPOM.artifact().ArtifactID != parentArtifact.ArtifactID { return nil, xerrors.New("'parent.relativePath' points at wrong local POM") } - result, err := p.analyze(pom, analysisOptions{}) - if err != nil { + if err := p.resolveParent(parsedPOM); err != nil { return nil, xerrors.Errorf("analyze error: %w", err) } - if !parentArtifact.Equal(result.artifact) { + if !parentArtifact.Equal(parsedPOM.artifact()) { return nil, xerrors.New("'parent.relativePath' points at wrong local POM") } - return pom, nil + return parsedPOM, nil } func (p *Parser) openRelativePom(currentPath, relativePath string) (*pom, error) { @@ -620,7 +615,7 @@ func (p *Parser) openPom(filePath string) (*pom, error) { } defer f.Close() - content, err := parsePom(f) + content, err := parsePom(f, false) if err != nil { return nil, xerrors.Errorf("failed to parse the local POM: %w", err) } @@ -708,7 +703,7 @@ func (p *Parser) remoteRepoRequest(repo string, paths []string) (*http.Request, paths = append([]string{repoURL.Path}, paths...) repoURL.Path = path.Join(paths...) - req, err := http.NewRequest("GET", repoURL.String(), http.NoBody) + req, err := http.NewRequest(http.MethodGet, repoURL.String(), http.NoBody) if err != nil { return nil, xerrors.Errorf("unable to create HTTP request: %w", err) } @@ -777,7 +772,7 @@ func (p *Parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom } defer resp.Body.Close() - content, err := parsePom(resp.Body) + content, err := parsePom(resp.Body, false) if err != nil { return nil, xerrors.Errorf("failed to parse the remote POM: %w", err) } @@ -788,13 +783,19 @@ func (p *Parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom }, nil } -func parsePom(r io.Reader) (*pomXML, error) { +func parsePom(r io.Reader, lineNumber bool) (*pomXML, error) { parsed := &pomXML{} decoder := xml.NewDecoder(r) decoder.CharsetReader = charset.NewReaderLabel if err := decoder.Decode(parsed); err != nil { return nil, xerrors.Errorf("xml decode error: %w", err) } + if !lineNumber { + for i := range parsed.Dependencies.Dependency { + parsed.Dependencies.Dependency[i].StartLine = 0 + parsed.Dependencies.Dependency[i].EndLine = 0 + } + } return parsed, nil } diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index 82604846a4f3..8d0bf002d6dc 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -14,6 +14,100 @@ import ( ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" ) +var ( + exampleNestedScopeCompile = func(start, end int) ftypes.Package { + var location ftypes.Locations + if start != 0 && end != 0 { + location = append(location, ftypes.Location{ + StartLine: start, + EndLine: end, + }) + } + return ftypes.Package{ + ID: "org.example:example-nested-scope-compile:1.0.0", + Name: "org.example:example-nested-scope-compile", + Version: "1.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: location, + } + } + + exampleNestedScopeEmpty = func(start, end int) ftypes.Package { + var location ftypes.Locations + if start != 0 && end != 0 { + location = append(location, ftypes.Location{ + StartLine: start, + EndLine: end, + }) + } + return ftypes.Package{ + ID: "org.example:example-nested-scope-empty:1.0.0", + Name: "org.example:example-nested-scope-empty", + Version: "1.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: location, + } + } + + exampleNestedScopeRuntime = func(start, end int) ftypes.Package { + var location ftypes.Locations + if start != 0 && end != 0 { + location = append(location, ftypes.Location{ + StartLine: start, + EndLine: end, + }) + } + return ftypes.Package{ + ID: "org.example:example-nested-scope-runtime:1.0.0", + Name: "org.example:example-nested-scope-runtime", + Version: "1.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: location, + } + } + + exampleScopeCompile = ftypes.Package{ + ID: "org.example:example-scope-compile:2.0.0", + Name: "org.example:example-scope-compile", + Version: "2.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleScopeEmpty = ftypes.Package{ + ID: "org.example:example-scope-empty:2.0.0", + Name: "org.example:example-scope-empty", + Version: "2.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleScopeRuntime = ftypes.Package{ + ID: "org.example:example-scope-runtime:2.0.0", + Name: "org.example:example-scope-runtime", + Version: "2.0.0", + Relationship: ftypes.RelationshipIndirect, + } + exampleApiCompile = ftypes.Package{ + ID: "org.example:example-api-compile:3.0.0", + Name: "org.example:example-api-compile", + Version: "3.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleApiEmpty = ftypes.Package{ + ID: "org.example:example-api-empty:3.0.0", + Name: "org.example:example-api-empty", + Version: "3.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleApiRuntime = ftypes.Package{ + ID: "org.example:example-api-runtime:3.0.0", + Name: "org.example:example-api-runtime", + Version: "3.0.0", + Relationship: ftypes.RelationshipIndirect, + } +) + func TestPom_Parse(t *testing.T) { tests := []struct { name string @@ -1630,6 +1724,320 @@ func TestPom_Parse(t *testing.T) { }, }, }, + // [INFO] com.example:child-depManagement-in-parent:jar:1.0.0 + // [INFO] +- org.example:example-api2:jar:1.0.2:runtime + // [INFO] +- org.example:example-api3:jar:4.0.3:compile + // [INFO] \- org.example:example-api:jar:1.0.1:compile + { + name: "dependency from parent uses version from child(scanned) pom depManagement", + inputFile: filepath.Join("testdata", "use-child-dep-management-in-parent", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:child-depManagement-in-parent:1.0.0", + Name: "com.example:child-depManagement-in-parent", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-api:1.0.1", + Name: "org.example:example-api", + Version: "1.0.1", + Relationship: ftypes.RelationshipDirect, + }, + { + ID: "org.example:example-api2:1.0.2", + Name: "org.example:example-api2", + Version: "1.0.2", + Relationship: ftypes.RelationshipDirect, + }, + { + ID: "org.example:example-api3:4.0.3", + Name: "org.example:example-api3", + Version: "4.0.3", + Relationship: ftypes.RelationshipDirect, + }, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:child-depManagement-in-parent:1.0.0", + DependsOn: []string{ + "org.example:example-api2:1.0.2", + "org.example:example-api3:4.0.3", + "org.example:example-api:1.0.1", + }, + }, + }, + }, + // [INFO] com.example:inherit-scopes-from-child-deps-and-their-parents:jar:0.0.1 + // [INFO] +- org.example:example-nested-scope-runtime:jar:1.0.0:runtime + // [INFO] | \- org.example:example-scope-runtime:jar:2.0.0:runtime + // [INFO] | \- org.example:example-api-runtime:jar:3.0.0:runtime + // [INFO] +- org.example:example-nested-scope-compile:jar:1.0.0:compile + // [INFO] | \- org.example:example-scope-compile:jar:2.0.0:compile + // [INFO] | \- org.example:example-api-compile:jar:3.0.0:compile + // [INFO] \- org.example:example-nested-scope-empty:jar:1.0.0:compile + // [INFO] \- org.example:example-scope-empty:jar:2.0.0:compile + // [INFO] \- org.example:example-api-empty:jar:3.0.0:compile + // + // `example-nested-*" dependencies and their parents contain `dependencyManagement` with changed scopes + { + name: "inherit scopes from child dependencies and their parents", + inputFile: filepath.Join("testdata", "inherit-scopes-from-child-deps-and-their-parents", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:inherit-scopes-from-child-deps-and-their-parents:0.0.1", + Name: "com.example:inherit-scopes-from-child-deps-and-their-parents", + Version: "0.0.1", + Relationship: ftypes.RelationshipRoot, + }, + exampleNestedScopeCompile(16, 21), + exampleNestedScopeEmpty(22, 26), + exampleNestedScopeRuntime(10, 15), + exampleApiCompile, + exampleApiEmpty, + exampleApiRuntime, + exampleScopeCompile, + exampleScopeEmpty, + exampleScopeRuntime, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:inherit-scopes-from-child-deps-and-their-parents:0.0.1", + DependsOn: []string{ + "org.example:example-nested-scope-compile:1.0.0", + "org.example:example-nested-scope-empty:1.0.0", + "org.example:example-nested-scope-runtime:1.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-compile:1.0.0", + DependsOn: []string{ + "org.example:example-scope-compile:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-empty:1.0.0", + DependsOn: []string{ + "org.example:example-scope-empty:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-runtime:1.0.0", + DependsOn: []string{ + "org.example:example-scope-runtime:2.0.0", + }, + }, + { + ID: "org.example:example-scope-compile:2.0.0", + DependsOn: []string{ + "org.example:example-api-compile:3.0.0", + }, + }, + { + ID: "org.example:example-scope-empty:2.0.0", + DependsOn: []string{ + "org.example:example-api-empty:3.0.0", + }, + }, + { + ID: "org.example:example-scope-runtime:2.0.0", + DependsOn: []string{ + "org.example:example-api-runtime:3.0.0", + }, + }, + }, + }, + // [INFO] com.example:inherit-scopes-in-parents-from-root:jar:0.1.0 + // [INFO] +- org.example:example-nested-scope-runtime:jar:1.0.0:runtime + // [INFO] | \- org.example:example-scope-runtime:jar:2.0.0:compile + // [INFO] | \- org.example:example-api-runtime:jar:3.0.0:runtime + // [INFO] +- org.example:example-nested-scope-compile:jar:1.0.0:compile + // [INFO] | \- org.example:example-scope-compile:jar:2.0.0:runtime + // [INFO] | \- org.example:example-api-compile:jar:3.0.0:test + // [INFO] \- org.example:example-nested-scope-empty:jar:1.0.0:compile + // [INFO] \- org.example:example-scope-empty:jar:2.0.0:runtime + // [INFO] \- org.example:example-api-empty:jar:3.0.0:test + // + // `example-nested-*" dependencies and their parents contain `dependencyManagement` with changed scopes + // scopes from `dependencyManagement` of root pom are used + { + name: "inherit scopes in children from root pom", + inputFile: filepath.Join("testdata", "inherit-scopes-in-children-from-root", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:inherit-scopes-in-children-from-root:0.0.1", + Name: "com.example:inherit-scopes-in-children-from-root", + Version: "0.0.1", + Relationship: ftypes.RelationshipRoot, + }, + exampleNestedScopeCompile(51, 56), + exampleNestedScopeEmpty(57, 61), + exampleNestedScopeRuntime(45, 50), + exampleApiRuntime, + exampleScopeCompile, + exampleScopeEmpty, + exampleScopeRuntime, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:inherit-scopes-in-children-from-root:0.0.1", + DependsOn: []string{ + "org.example:example-nested-scope-compile:1.0.0", + "org.example:example-nested-scope-empty:1.0.0", + "org.example:example-nested-scope-runtime:1.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-compile:1.0.0", + DependsOn: []string{ + "org.example:example-scope-compile:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-empty:1.0.0", + DependsOn: []string{ + "org.example:example-scope-empty:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-runtime:1.0.0", + DependsOn: []string{ + "org.example:example-scope-runtime:2.0.0", + }, + }, + { + ID: "org.example:example-scope-runtime:2.0.0", + DependsOn: []string{ + "org.example:example-api-runtime:3.0.0", + }, + }, + }, + }, + // [INFO] com.example:inherit-scopes-in-parents-from-root:jar:0.1.0 + // [INFO] +- org.example:example-nested-scope-runtime:jar:1.0.0:runtime + // [INFO] | \- org.example:example-scope-runtime:jar:2.0.0:compile + // [INFO] | \- org.example:example-api-runtime:jar:3.0.0:runtime + // [INFO] +- org.example:example-nested-scope-compile:jar:1.0.0:compile + // [INFO] | \- org.example:example-scope-compile:jar:2.0.0:runtime + // [INFO] | \- org.example:example-api-compile:jar:3.0.0:test + // [INFO] \- org.example:example-nested-scope-empty:jar:1.0.0:test + // [INFO] \- org.example:example-scope-empty:jar:2.0.0:test + // [INFO] \- org.example:example-api-empty:jar:3.0.0:test + // + // `example-nested-*" dependencies and their parents contain `dependencyManagement` with changed scopes + // scopes from `dependencyManagement` of root pom are used in parent dependencies + { + name: "inherit scopes in parent from root pom", + inputFile: filepath.Join("testdata", "inherit-scopes-in-parents-from-root", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:inherit-scopes-in-parents-from-root:0.1.0", + Name: "com.example:inherit-scopes-in-parents-from-root", + Version: "0.1.0", + Relationship: ftypes.RelationshipRoot, + }, + exampleNestedScopeCompile(0, 0), + exampleNestedScopeRuntime(0, 0), + exampleApiRuntime, + exampleScopeCompile, + exampleScopeRuntime, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:inherit-scopes-in-parents-from-root:0.1.0", + DependsOn: []string{ + "org.example:example-nested-scope-compile:1.0.0", + "org.example:example-nested-scope-runtime:1.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-compile:1.0.0", + DependsOn: []string{ + "org.example:example-scope-compile:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-runtime:1.0.0", + DependsOn: []string{ + "org.example:example-scope-runtime:2.0.0", + }, + }, + { + ID: "org.example:example-scope-runtime:2.0.0", + DependsOn: []string{ + "org.example:example-api-runtime:3.0.0", + }, + }, + }, + }, + //[INFO] com.example:root-pom-with-spaces:jar:1.0.0 + //[INFO] \- org.example:example-nested:jar:3.3.3:compile + //[INFO] \- org.example:example-dependency:jar:1.2.4:compile + //[INFO] \- org.example:example-api:jar:2.0.0:compile + { + name: "space at the start and/or end of the text nodes", + inputFile: filepath.Join("testdata", "with-spaces", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:root-pom-with-spaces:1.0.0", + Name: "com.example:root-pom-with-spaces", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-nested:3.3.3", + Name: "org.example:example-nested", + Version: "3.3.3", + Relationship: ftypes.RelationshipDirect, + Locations: ftypes.Locations{ + { + StartLine: 24, + EndLine: 28, + }, + }, + }, + { + ID: "org.example:example-api:2.0.0", + Name: "org.example:example-api", + Version: "2.0.0", + Licenses: []string{"The Apache Software License, Version 2.0"}, + Relationship: ftypes.RelationshipIndirect, + }, + // dependency version is taken from `com.example:root-pom-with-spaces` from dependencyManagement + // not from `com.example:example-nested` from `com.example:example-nested` + { + ID: "org.example:example-dependency:1.2.4", + Name: "org.example:example-dependency", + Version: "1.2.4", + Relationship: ftypes.RelationshipIndirect, + }, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:root-pom-with-spaces:1.0.0", + DependsOn: []string{ + "org.example:example-nested:3.3.3", + }, + }, + { + ID: "org.example:example-dependency:1.2.4", + DependsOn: []string{ + "org.example:example-api:2.0.0", + }, + }, + { + ID: "org.example:example-nested:3.3.3", + DependsOn: []string{ + "org.example:example-dependency:1.2.4", + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/dependency/parser/java/pom/pom.go b/pkg/dependency/parser/java/pom/pom.go index 889d107c3c6c..83c5d4fec609 100644 --- a/pkg/dependency/parser/java/pom/pom.go +++ b/pkg/dependency/parser/java/pom/pom.go @@ -23,11 +23,18 @@ type pom struct { content *pomXML } -func (p *pom) inherit(result analysisResult) { +func (p *pom) nil() bool { + return p == nil || p.content == nil +} + +func (p *pom) inherit(parent *pom) { + if parent == nil { + return + } // Merge properties - p.content.Properties = utils.MergeMaps(result.properties, p.content.Properties) + p.content.Properties = utils.MergeMaps(parent.properties(), p.content.Properties) - art := p.artifact().Inherit(result.artifact) + art := p.artifact().Inherit(parent.artifact()) p.content.GroupId = art.GroupID p.content.ArtifactId = art.ArtifactID @@ -40,12 +47,12 @@ func (p *pom) inherit(result analysisResult) { } } -func (p pom) properties() properties { +func (p *pom) properties() properties { props := p.content.Properties return utils.MergeMaps(props, p.projectProperties()) } -func (p pom) projectProperties() map[string]string { +func (p *pom) projectProperties() map[string]string { val := reflect.ValueOf(p.content).Elem() props := p.listProperties(val) @@ -73,7 +80,7 @@ func (p pom) projectProperties() map[string]string { return projectProperties } -func (p pom) listProperties(val reflect.Value) map[string]string { +func (p *pom) listProperties(val reflect.Value) map[string]string { props := make(map[string]string) for i := 0; i < val.NumField(); i++ { f := val.Type().Field(i) @@ -106,17 +113,17 @@ func (p pom) listProperties(val reflect.Value) map[string]string { return props } -func (p pom) artifact() artifact { +func (p *pom) artifact() artifact { return newArtifact(p.content.GroupId, p.content.ArtifactId, p.content.Version, p.licenses(), p.content.Properties) } -func (p pom) licenses() []string { +func (p *pom) licenses() []string { return slices.ZeroToNil(lo.FilterMap(p.content.Licenses.License, func(lic pomLicense, _ int) (string, bool) { return lic.Name, lic.Name != "" })) } -func (p pom) repositories(servers []Server) ([]string, []string) { +func (p *pom) repositories(servers []Server) ([]string, []string) { logger := log.WithPrefix("pom") var releaseRepos, snapshotRepos []string for _, rep := range p.content.Repositories.Repository { @@ -242,9 +249,11 @@ func (d pomDependency) Resolve(props map[string]string, depManagement, rootDepMa if managed.Version != "" { dep.Version = evaluateVariable(managed.Version, props, nil) } + if managed.Scope != "" { dep.Scope = evaluateVariable(managed.Scope, props, nil) } + if managed.Optional { dep.Optional = managed.Optional } @@ -287,7 +296,7 @@ func (d pomDependency) ToArtifact(opts analysisOptions) artifact { } var locations ftypes.Locations - if opts.lineNumber { + if d.StartLine != 0 && d.EndLine != 0 { locations = ftypes.Locations{ { StartLine: d.StartLine, diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-from-child-deps-and-their-parents/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-from-child-deps-and-their-parents/pom.xml new file mode 100644 index 000000000000..17e6c55de090 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-from-child-deps-and-their-parents/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + + com.example + inherit-scopes-from-child-deps-and-their-parents + 0.0.1 + + + + org.example + example-nested-scope-runtime + 1.0.0 + runtime + + + org.example + example-nested-scope-compile + 1.0.0 + compile + + + org.example + example-nested-scope-empty + 1.0.0 + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-children-from-root/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-children-from-root/pom.xml new file mode 100644 index 000000000000..26bdb25621d7 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-children-from-root/pom.xml @@ -0,0 +1,64 @@ + + 4.0.0 + + com.example + inherit-scopes-in-children-from-root + 0.0.1 + + + + + org.example + example-scope-runtime + 2.0.0 + compile + + + org.example + example-scope-compile + 2.0.0 + runtime + + + org.example + example-api-compile + 3.0.0 + test + + + org.example + example-scope-empty + 2.0.0 + runtime + + + org.example + example-api-empty + 3.0.0 + test + + + + + + + org.example + example-nested-scope-runtime + 1.0.0 + runtime + + + org.example + example-nested-scope-compile + 1.0.0 + compile + + + org.example + example-nested-scope-empty + 1.0.0 + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/parent/pom.xml new file mode 100644 index 000000000000..dad0101e3f25 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/parent/pom.xml @@ -0,0 +1,77 @@ + + 4.0.0 + + com.example + inherit-scopes-in-parents-from-root-parent + 0.0.1 + pom + + + + + org.example + example-scope-runtime + 2.0.0 + compile + + + org.example + example-scope-compile + 2.0.0 + runtime + + + org.example + example-api-compile + 3.0.0 + test + + + org.example + example-scope-empty + 2.0.0 + test + + + org.example + example-api-empty + 3.0.0 + test + + + org.example + example-nested-scope-compile + 1.0.0 + runtime + + + org.example + example-nested-scope-empty + 1.0.0 + compile + + + + + + + org.example + example-nested-scope-runtime + 1.0.0 + runtime + + + org.example + example-nested-scope-compile + 1.0.0 + compile + + + org.example + example-nested-scope-empty + 1.0.0 + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/pom.xml new file mode 100644 index 000000000000..45ef37785e60 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + com.example + inherit-scopes-in-parents-from-root + 0.1.0 + + + com.example + inherit-scopes-in-parents-from-root-parent + 0.0.1 + ./parent + + + + + + org.example + example-nested-scope-runtime + 1.0.0 + test + + + org.example + example-nested-scope-compile + 1.0.0 + test + + + org.example + example-nested-scope-empty + 1.0.0 + test + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/example-nested-scope-compile-1.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/example-nested-scope-compile-1.0.0.pom new file mode 100644 index 000000000000..43ca478a88cd --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/example-nested-scope-compile-1.0.0.pom @@ -0,0 +1,41 @@ + + + + 4.0.0 + + + com.example + example-nested-parent-scope-compile + 1.0.1 + ./parent + + + org.example + example-nested-scope-compile + 1.0.0 + + jar + Example pom with example-scope-compile 2.0.0 + + + + + org.example + example-api-compile + 3.0.0 + runtime + + + + + + + org.example + example-scope-compile + 2.0.0 + compile + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/parent/pom.xml new file mode 100644 index 000000000000..5fe41b350df1 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/parent/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + com.example + example-nested-parent-scope-compile + 1.0.1 + pom + + + + + org.example + example-scope-compile + 2.0.0 + test + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/example-nested-scope-empty-1.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/example-nested-scope-empty-1.0.0.pom new file mode 100644 index 000000000000..b1def2383f79 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/example-nested-scope-empty-1.0.0.pom @@ -0,0 +1,40 @@ + + + + 4.0.0 + + + com.example + example-nested-parent-scope-empty + 1.0.1 + ./parent + + + org.example + example-nested-scope-empty + 1.0.0 + + jar + Example pom with example-scope-empty 2.0.0 + + + + + org.example + example-api-empty + 3.0.0 + test + + + + + + + org.example + example-scope-empty + 2.0.0 + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/parent/pom.xml new file mode 100644 index 000000000000..6ca0232f4fc3 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/parent/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + com.example + example-nested-parent-scope-empty + 1.0.1 + pom + + + + + org.example + example-scope-empty + 2.0.0 + compile + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/example-nested-scope-runtime-1.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/example-nested-scope-runtime-1.0.0.pom new file mode 100644 index 000000000000..8e48dcd812c3 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/example-nested-scope-runtime-1.0.0.pom @@ -0,0 +1,41 @@ + + + + 4.0.0 + + + com.example + example-nested-parent-scope-runtime + 1.0.1 + ./parent + + + org.example + example-nested-scope-runtime + 1.0.0 + + jar + Example pom with example-scope-runtime 2.0.0 + + + + + org.example + example-api-runtime + 3.0.0 + test + + + + + + + org.example + example-scope-runtime + 2.0.0 + runtime + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/parent/pom.xml new file mode 100644 index 000000000000..f59df7902390 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/parent/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + com.example + example-nested-parent-scope-runtime + 1.0.1 + pom + + + + + org.example + example-scope-runtime + 2.0.0 + test + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-compile/2.0.0/example-scope-compile-2.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-compile/2.0.0/example-scope-compile-2.0.0.pom new file mode 100644 index 000000000000..1d5bc02ff3b9 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-compile/2.0.0/example-scope-compile-2.0.0.pom @@ -0,0 +1,23 @@ + + + + 4.0.0 + + org.example + example-scope-compile + 2.0.0 + + jar + Example pom with example-api-compile 3.0.0 + + + + org.example + example-api-compile + 3.0.0 + compile + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-empty/2.0.0/example-scope-empty-2.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-empty/2.0.0/example-scope-empty-2.0.0.pom new file mode 100644 index 000000000000..82ce31008fc3 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-empty/2.0.0/example-scope-empty-2.0.0.pom @@ -0,0 +1,22 @@ + + + + 4.0.0 + + org.example + example-scope-empty + 2.0.0 + + jar + Example pom with example-api-empty 3.0.0 + + + + org.example + example-api-empty + 3.0.0 + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-runtime/2.0.0/example-scope-runtime-2.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-runtime/2.0.0/example-scope-runtime-2.0.0.pom new file mode 100644 index 000000000000..90bf973def80 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-runtime/2.0.0/example-scope-runtime-2.0.0.pom @@ -0,0 +1,23 @@ + + + + 4.0.0 + + org.example + example-scope-runtime + 2.0.0 + + jar + Example pom with example-api-runtime 3.0.0 + + + + org.example + example-api-runtime + 3.0.0 + runtime + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml b/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml index 5662c0c9e8de..ae1d6ced6e3f 100644 --- a/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml +++ b/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml @@ -13,7 +13,7 @@ com.example parent 3.0.0 - ../parent + ../ diff --git a/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/parent/pom.xml new file mode 100644 index 000000000000..dec887e6eecc --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/parent/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + + org.example + example-top-parent + 5.0.0 + ../top-parent/pom.xml + + + org.example + example-parent + 4.0.0 + pom + + + 4.0.1 + + + + org.example + example-api2 + + + org.example + example-api3 + 4.0.3 + compile + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/pom.xml new file mode 100644 index 000000000000..651fbba9f885 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + + org.example + example-parent + 4.0.0 + ./parent/pom.xml + + + com.example + child-depManagement-in-parent + 1.0.0 + + + + 1.0.1 + 1.0.2 + 1.0.3 + + + + + + org.example + example-api + ${api.version} + runtime + + + org.example + example-api2 + ${api2.version} + runtime + + + org.example + example-api3 + ${api3.version} + runtime + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/top-parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/top-parent/pom.xml new file mode 100644 index 000000000000..84d8954a4bf9 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/top-parent/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + org.example + example-top-parent + 5.0.0 + pom + + + 5.0.1 + + + + + org.example + example-api + ${api.version} + compile + + + org.example + example-api2 + + + org.example + example-api3 + 5.0.3 + compile + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/with-spaces/pom.xml b/pkg/dependency/parser/java/pom/testdata/with-spaces/pom.xml new file mode 100644 index 000000000000..7a0a1013aa31 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/with-spaces/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + com.example + root-pom-with-spaces + 1.0.0 + + + 1.2.4 + + + + + + org.example + example-dependency + ${dependency.version} + + + + + + + org.example + example-nested + 3.3.3 + + + + diff --git a/pkg/detector/ospkg/ubuntu/ubuntu.go b/pkg/detector/ospkg/ubuntu/ubuntu.go index b396d530d84e..8b36a2b1f87e 100644 --- a/pkg/detector/ospkg/ubuntu/ubuntu.go +++ b/pkg/detector/ospkg/ubuntu/ubuntu.go @@ -62,6 +62,7 @@ var ( "23.04": time.Date(2024, 1, 20, 23, 59, 59, 0, time.UTC), "23.10": time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC), "24.04": time.Date(2034, 3, 31, 23, 59, 59, 0, time.UTC), + "24.10": time.Date(2025, 7, 9, 23, 59, 59, 0, time.UTC), } ) diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go index c79a81f54c61..35a4fc12fea1 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go @@ -16,8 +16,11 @@ import ( "github.com/aquasecurity/trivy/pkg/misconf" ) -var disabledChecks = []string{ - "DS016", // See https://github.com/aquasecurity/trivy/issues/7368 +var disabledChecks = []misconf.DisabledCheck{ + { + ID: "DS016", Scanner: string(analyzer.TypeHistoryDockerfile), + Reason: "See https://github.com/aquasecurity/trivy/issues/7368", + }, } const analyzerVersion = 1 @@ -31,7 +34,7 @@ type historyAnalyzer struct { } func newHistoryAnalyzer(opts analyzer.ConfigAnalyzerOptions) (analyzer.ConfigAnalyzer, error) { - opts.MisconfScannerOption.DisabledCheckIDs = append(opts.MisconfScannerOption.DisabledCheckIDs, disabledChecks...) + opts.MisconfScannerOption.DisabledChecks = append(opts.MisconfScannerOption.DisabledChecks, disabledChecks...) s, err := misconf.NewScanner(detection.FileTypeDockerfile, opts.MisconfScannerOption) if err != nil { return nil, xerrors.Errorf("misconfiguration scanner error: %w", err) diff --git a/pkg/fanal/analyzer/language/golang/binary/binary_test.go b/pkg/fanal/analyzer/language/golang/binary/binary_test.go index 70256113cfd3..041d43e1b45b 100644 --- a/pkg/fanal/analyzer/language/golang/binary/binary_test.go +++ b/pkg/fanal/analyzer/language/golang/binary/binary_test.go @@ -36,7 +36,7 @@ func Test_gobinaryLibraryAnalyzer_Analyze(t *testing.T) { }, { Name: "stdlib", - Version: "1.15.2", + Version: "v1.15.2", Relationship: types.RelationshipDirect, }, { diff --git a/pkg/fanal/analyzer/language/golang/mod/mod.go b/pkg/fanal/analyzer/language/golang/mod/mod.go index 398511fdc63c..52d7b32f3bee 100644 --- a/pkg/fanal/analyzer/language/golang/mod/mod.go +++ b/pkg/fanal/analyzer/language/golang/mod/mod.go @@ -148,7 +148,7 @@ func (a *gomodAnalyzer) fillAdditionalData(apps []types.Application) error { } // e.g. $GOPATH/pkg/mod/github.com/aquasecurity/go-dep-parser@v1.0.0 - modDir := filepath.Join(modPath, fmt.Sprintf("%s@v%s", normalizeModName(lib.Name), lib.Version)) + modDir := filepath.Join(modPath, fmt.Sprintf("%s@%s", normalizeModName(lib.Name), lib.Version)) // Collect licenses if licenseNames, err := findLicense(modDir, a.licenseClassifierConfidenceLevel); err != nil { diff --git a/pkg/fanal/analyzer/language/golang/mod/mod_test.go b/pkg/fanal/analyzer/language/golang/mod/mod_test.go index 5cf006430aca..3963bcebbad9 100644 --- a/pkg/fanal/analyzer/language/golang/mod/mod_test.go +++ b/pkg/fanal/analyzer/language/golang/mod/mod_test.go @@ -46,7 +46,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20220406074731-71021a481237", + Version: "v0.0.0-20220406074731-71021a481237", Relationship: types.RelationshipDirect, Licenses: []string{"MIT"}, ExternalReferences: []types.ExternalRef{ @@ -62,7 +62,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: types.RelationshipIndirect, Indirect: true, }, @@ -96,7 +96,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/sad/sad@v0.0.1", Name: "github.com/sad/sad", - Version: "0.0.1", + Version: "v0.0.1", Relationship: types.RelationshipDirect, ExternalReferences: []types.ExternalRef{ { @@ -136,7 +136,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20230219131432-590b1dfb6edd", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20230219131432-590b1dfb6edd", + Version: "v0.0.0-20230219131432-590b1dfb6edd", Relationship: types.RelationshipDirect, DependsOn: []string{ "github.com/BurntSushi/toml@v0.3.1", @@ -151,7 +151,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/BurntSushi/toml@v0.3.1", Name: "github.com/BurntSushi/toml", - Version: "0.3.1", + Version: "v0.3.1", Relationship: types.RelationshipIndirect, Indirect: true, Licenses: []string{ @@ -188,7 +188,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20230219131432-590b1dfb6edd", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20230219131432-590b1dfb6edd", + Version: "v0.0.0-20230219131432-590b1dfb6edd", Relationship: types.RelationshipDirect, DependsOn: []string{}, ExternalReferences: []types.ExternalRef{ diff --git a/pkg/fanal/analyzer/language/java/jar/jar_test.go b/pkg/fanal/analyzer/language/java/jar/jar_test.go index 146b60c1bbd6..58e7221066ac 100644 --- a/pkg/fanal/analyzer/language/java/jar/jar_test.go +++ b/pkg/fanal/analyzer/language/java/jar/jar_test.go @@ -18,10 +18,6 @@ import ( _ "modernc.org/sqlite" ) -const ( - defaultJavaDBRepository = "ghcr.io/aquasecurity/trivy-java-db" -) - func Test_javaLibraryAnalyzer_Analyze(t *testing.T) { tests := []struct { name string diff --git a/pkg/fanal/analyzer/pkg/rpm/archive.go b/pkg/fanal/analyzer/pkg/rpm/archive.go index e9db78c6c648..2eda91e4a563 100644 --- a/pkg/fanal/analyzer/pkg/rpm/archive.go +++ b/pkg/fanal/analyzer/pkg/rpm/archive.go @@ -136,7 +136,14 @@ func (a *rpmArchiveAnalyzer) generatePURL(pkg *types.Package) *packageurl.Packag case strings.Contains(vendor, "suse"): ns = "suse" } - return packageurl.NewPackageURL(packageurl.TypeRPM, ns, pkg.Name, utils.FormatVersion(*pkg), nil, "") + var qualifiers packageurl.Qualifiers + if pkg.Arch != "" { + qualifiers = append(qualifiers, packageurl.Qualifier{ + Key: "arch", + Value: pkg.Arch, + }) + } + return packageurl.NewPackageURL(packageurl.TypeRPM, ns, pkg.Name, utils.FormatVersion(*pkg), qualifiers, "") } func (a *rpmArchiveAnalyzer) unexpectedError(err error) error { diff --git a/pkg/fanal/analyzer/pkg/rpm/archive_test.go b/pkg/fanal/analyzer/pkg/rpm/archive_test.go index b9ac0a44ee53..5e8475e171f6 100644 --- a/pkg/fanal/analyzer/pkg/rpm/archive_test.go +++ b/pkg/fanal/analyzer/pkg/rpm/archive_test.go @@ -52,6 +52,12 @@ func Test_rpmArchiveAnalyzer_Analyze(t *testing.T) { Namespace: "redhat", Name: "socat", Version: "1.7.3.2-2.el7", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "x86_64", + }, + }, }, }, }, diff --git a/pkg/fanal/artifact/repo/git.go b/pkg/fanal/artifact/repo/git.go index 8eb3d9af7f9d..4532b631c6b7 100644 --- a/pkg/fanal/artifact/repo/git.go +++ b/pkg/fanal/artifact/repo/git.go @@ -128,7 +128,7 @@ func cloneRepo(u *url.URL, artifactOpt artifact.Option) (string, error) { cloneOptions := git.CloneOptions{ URL: u.String(), Auth: gitAuth(), - Progress: os.Stdout, + Progress: os.Stderr, InsecureSkipTLS: artifactOpt.Insecure, } diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index d16ad3dac059..568af60528ea 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -27,6 +27,7 @@ import ( "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" + "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/applier" @@ -77,7 +78,7 @@ func startContainerd(t *testing.T, ctx context.Context, hostPath string) { t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") req := testcontainers.ContainerRequest{ Name: "containerd", - Image: "ghcr.io/aquasecurity/trivy-test-images/containerd:latest", + Image: testutil.ImageName("containerd", "latest", ""), Entrypoint: []string{ "/bin/sh", "-c", @@ -122,7 +123,7 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { digest := "sha256:f12582b2f2190f350e3904462c1c23aaf366b4f76705e97b199f9bbded1d816a" basename := "hello" tag := "world" - importedImageOriginalName := "ghcr.io/aquasecurity/trivy-test-images:alpine-310" + importedImageOriginalName := testutil.ImageName("", "alpine-310", "") tests := []struct { name string @@ -299,15 +300,15 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { }{ { name: "alpine 3.10", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), tarArchive: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", wantMetadata: artifact.ImageMetadata{ ID: "sha256:961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4", DiffIDs: []string{ "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0", }, - RepoTags: []string{"ghcr.io/aquasecurity/trivy-test-images:alpine-310"}, - RepoDigests: []string{"ghcr.io/aquasecurity/trivy-test-images@sha256:f12582b2f2190f350e3904462c1c23aaf366b4f76705e97b199f9bbded1d816a"}, + RepoTags: []string{testutil.ImageName("", "alpine-310", "")}, + RepoDigests: []string{testutil.ImageName("", "", "sha256:f12582b2f2190f350e3904462c1c23aaf366b4f76705e97b199f9bbded1d816a")}, ConfigFile: v1.ConfigFile{ Architecture: "amd64", Created: v1.Time{ @@ -347,7 +348,7 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { }, { name: "vulnimage", - imageName: "ghcr.io/aquasecurity/trivy-test-images:vulnimage", + imageName: testutil.ImageName("", "vulnimage", ""), tarArchive: "../../../../integration/testdata/fixtures/images/vulnimage.tar.gz", wantMetadata: artifact.ImageMetadata{ ID: "sha256:c17083664da903e13e9092fa3a3a1aeee2431aa2728298e3dbcec72f26369c41", @@ -373,8 +374,8 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { "sha256:ba17950e91742d6ac7055ea3a053fe764486658ca1ce8188f1e427b1fe2bc4da", "sha256:6ef42db7800507577383edf1937cb203b9b85f619feed6046594208748ceb52c", }, - RepoTags: []string{"ghcr.io/aquasecurity/trivy-test-images:vulnimage"}, - RepoDigests: []string{"ghcr.io/aquasecurity/trivy-test-images@sha256:e74abbfd81e00baaf464cf9e09f8b24926e5255171e3150a60aa341ce064688f"}, + RepoTags: []string{testutil.ImageName("", "vulnimage", "")}, + RepoDigests: []string{testutil.ImageName("", "", "sha256:e74abbfd81e00baaf464cf9e09f8b24926e5255171e3150a60aa341ce064688f")}, ConfigFile: v1.ConfigFile{ Architecture: "amd64", Created: v1.Time{ @@ -750,14 +751,14 @@ func TestContainerd_PullImage(t *testing.T) { }{ { name: "remote alpine 3.10", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), wantMetadata: artifact.ImageMetadata{ ID: "sha256:961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4", DiffIDs: []string{ "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0", }, - RepoTags: []string{"ghcr.io/aquasecurity/trivy-test-images:alpine-310"}, - RepoDigests: []string{"ghcr.io/aquasecurity/trivy-test-images@sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb"}, + RepoTags: []string{testutil.ImageName("", "alpine-310", "")}, + RepoDigests: []string{testutil.ImageName("", "", "sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb")}, ConfigFile: v1.ConfigFile{ Architecture: "amd64", Created: v1.Time{ diff --git a/pkg/fanal/test/integration/registry_test.go b/pkg/fanal/test/integration/registry_test.go index 5b062e425729..4bca93f0ccba 100644 --- a/pkg/fanal/test/integration/registry_test.go +++ b/pkg/fanal/test/integration/registry_test.go @@ -20,6 +20,7 @@ import ( testcontainers "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" + "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/all" @@ -95,7 +96,7 @@ func TestTLSRegistry(t *testing.T) { }{ { name: "happy path", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ @@ -120,7 +121,7 @@ func TestTLSRegistry(t *testing.T) { }, { name: "happy path with docker login", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ @@ -140,7 +141,7 @@ func TestTLSRegistry(t *testing.T) { }, { name: "sad path: tls verify", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ @@ -156,7 +157,7 @@ func TestTLSRegistry(t *testing.T) { }, { name: "sad path: no credential", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ diff --git a/pkg/fanal/types/misconf.go b/pkg/fanal/types/misconf.go index 3b4e9b447895..706220f0a1c7 100644 --- a/pkg/fanal/types/misconf.go +++ b/pkg/fanal/types/misconf.go @@ -8,13 +8,12 @@ import ( ) type Misconfiguration struct { - FileType ConfigType `json:",omitempty"` - FilePath string `json:",omitempty"` - Successes MisconfResults `json:",omitempty"` - Warnings MisconfResults `json:",omitempty"` - Failures MisconfResults `json:",omitempty"` - Exceptions MisconfResults `json:",omitempty"` - Layer Layer `json:",omitempty"` + FileType ConfigType `json:",omitempty"` + FilePath string `json:",omitempty"` + Successes MisconfResults `json:",omitempty"` + Warnings MisconfResults `json:",omitempty"` + Failures MisconfResults `json:",omitempty"` + Layer Layer `json:",omitempty"` } type MisconfResult struct { @@ -117,7 +116,6 @@ func ToMisconfigurations(misconfs map[string]Misconfiguration) []Misconfiguratio sort.Sort(misconf.Successes) sort.Sort(misconf.Warnings) sort.Sort(misconf.Failures) - sort.Sort(misconf.Exceptions) results = append(results, misconf) } diff --git a/pkg/flag/misconf_flags.go b/pkg/flag/misconf_flags.go index 128ecfcac42e..421e9c899285 100644 --- a/pkg/flag/misconf_flags.go +++ b/pkg/flag/misconf_flags.go @@ -34,7 +34,7 @@ var ( IncludeNonFailuresFlag = Flag[bool]{ Name: "include-non-failures", ConfigName: "misconfiguration.include-non-failures", - Usage: "include successes and exceptions, available with '--scanners misconfig'", + Usage: "include successes, available with '--scanners misconfig'", } HelmValuesFileFlag = Flag[[]string]{ Name: "helm-values", diff --git a/pkg/flag/registry_flags.go b/pkg/flag/registry_flags.go index 552eaf4276d6..9c03a07b8872 100644 --- a/pkg/flag/registry_flags.go +++ b/pkg/flag/registry_flags.go @@ -1,6 +1,8 @@ package flag import ( + "io" + "os" "strings" "golang.org/x/xerrors" @@ -19,6 +21,11 @@ var ( ConfigName: "registry.password", Usage: "password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.", } + PasswordStdinFlag = Flag[bool]{ + Name: "password-stdin", + ConfigName: "registry.password-stdin", + Usage: "password from stdin. Comma-separated passwords are not supported.", + } RegistryTokenFlag = Flag[string]{ Name: "registry-token", ConfigName: "registry.token", @@ -29,6 +36,7 @@ var ( type RegistryFlagGroup struct { Username *Flag[[]string] Password *Flag[[]string] + PasswordStdin *Flag[bool] RegistryToken *Flag[string] } @@ -41,6 +49,7 @@ func NewRegistryFlagGroup() *RegistryFlagGroup { return &RegistryFlagGroup{ Username: UsernameFlag.Clone(), Password: PasswordFlag.Clone(), + PasswordStdin: PasswordStdinFlag.Clone(), RegistryToken: RegistryTokenFlag.Clone(), } } @@ -53,6 +62,7 @@ func (f *RegistryFlagGroup) Flags() []Flagger { return []Flagger{ f.Username, f.Password, + f.PasswordStdin, f.RegistryToken, } } @@ -65,8 +75,19 @@ func (f *RegistryFlagGroup) ToOptions() (RegistryOptions, error) { var credentials []types.Credential users := f.Username.Value() passwords := f.Password.Value() + if f.PasswordStdin.Value() { + if len(passwords) != 0 { + return RegistryOptions{}, xerrors.New("'--password' and '--password-stdin' can't be used at the same time") + } + contents, err := io.ReadAll(os.Stdin) + if err != nil { + return RegistryOptions{}, xerrors.Errorf("failed to read from stdin: %w", err) + } + // "--password-stdin" doesn't support comma-separated passwords + passwords = []string{strings.TrimRight(string(contents), "\r\n")} + } if len(users) != len(passwords) { - return RegistryOptions{}, xerrors.New("the length of usernames and passwords must match") + return RegistryOptions{}, xerrors.New("the number of usernames and passwords must match") } for i, user := range users { credentials = append(credentials, types.Credential{ diff --git a/pkg/flag/rego_flags.go b/pkg/flag/rego_flags.go index 4b291f0a5eb3..84e0aed910f4 100644 --- a/pkg/flag/rego_flags.go +++ b/pkg/flag/rego_flags.go @@ -19,6 +19,7 @@ var ( Aliases: []Alias{ { Name: "skip-policy-update", + ConfigName: "rego.skip-policy-update", Deprecated: true, }, }, diff --git a/pkg/flag/report_flags.go b/pkg/flag/report_flags.go index 67d553b65553..d69443e89547 100644 --- a/pkg/flag/report_flags.go +++ b/pkg/flag/report_flags.go @@ -6,6 +6,7 @@ import ( "github.com/mattn/go-shellwords" "github.com/samber/lo" + "github.com/spf13/viper" "golang.org/x/xerrors" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" @@ -14,6 +15,7 @@ import ( "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/result" "github.com/aquasecurity/trivy/pkg/types" + "github.com/aquasecurity/trivy/pkg/utils/fsutils" xstrings "github.com/aquasecurity/trivy/pkg/x/strings" ) @@ -238,6 +240,10 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { } } + if viper.IsSet(f.IgnoreFile.ConfigName) && !fsutils.FileExists(f.IgnoreFile.Value()) { + return ReportOptions{}, xerrors.Errorf("ignore file not found: %s", f.IgnoreFile.Value()) + } + return ReportOptions{ Format: format, ReportFormat: f.ReportFormat.Value(), diff --git a/pkg/flag/report_flags_test.go b/pkg/flag/report_flags_test.go index 9440bf373905..b113d7c62f97 100644 --- a/pkg/flag/report_flags_test.go +++ b/pkg/flag/report_flags_test.go @@ -209,4 +209,16 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { assert.Equal(t, tt.wantLogs, out.Messages(), tt.name) }) } + + t.Run("Error on non existing ignore file", func(t *testing.T) { + t.Cleanup(viper.Reset) + + setValue(flag.IgnoreFileFlag.ConfigName, string("doesntexist")) + f := &flag.ReportFlagGroup{ + IgnoreFile: flag.IgnoreFileFlag.Clone(), + } + + _, err := f.ToOptions() + assert.ErrorContains(t, err, "ignore file not found: doesntexist") + }) } diff --git a/pkg/iac/adapters/arm/storage/adapt.go b/pkg/iac/adapters/arm/storage/adapt.go index 018949e24e10..26a0d10c8616 100644 --- a/pkg/iac/adapters/arm/storage/adapt.go +++ b/pkg/iac/adapters/arm/storage/adapt.go @@ -18,20 +18,18 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account { var accounts []storage.Account for _, resource := range deployment.GetResourcesByType("Microsoft.Storage/storageAccounts") { - var networkRules []storage.NetworkRule - for _, acl := range resource.Properties.GetMapValue("networkAcls").AsList() { + acl := resource.Properties.GetMapValue("networkAcls") - var bypasses []types.StringValue - bypassProp := acl.GetMapValue("bypass") - for _, bypass := range strings.Split(bypassProp.AsString(), ",") { - bypasses = append(bypasses, types.String(bypass, bypassProp.GetMetadata())) - } + var bypasses []types.StringValue + bypassProp := acl.GetMapValue("bypass") + for _, bypass := range strings.Split(bypassProp.AsString(), ",") { + bypasses = append(bypasses, types.String(strings.TrimSpace(bypass), bypassProp.GetMetadata())) + } - networkRules = append(networkRules, storage.NetworkRule{ - Metadata: acl.GetMetadata(), - Bypass: bypasses, - AllowByDefault: types.Bool(acl.GetMapValue("defaultAction").EqualTo("Allow"), acl.GetMetadata()), - }) + networkRule := storage.NetworkRule{ + Metadata: acl.GetMetadata(), + Bypass: bypasses, + AllowByDefault: types.Bool(acl.GetMapValue("defaultAction").EqualTo("Allow"), acl.GetMetadata()), } var queues []storage.Queue @@ -52,7 +50,7 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account { account := storage.Account{ Metadata: resource.Metadata, - NetworkRules: networkRules, + NetworkRules: []storage.NetworkRule{networkRule}, EnforceHTTPS: resource.Properties.GetMapValue("supportsHttpsTrafficOnly").AsBoolValue(false, resource.Properties.GetMetadata()), Containers: containers, QueueProperties: storage.QueueProperties{ @@ -62,6 +60,12 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account { MinimumTLSVersion: resource.Properties.GetMapValue("minimumTlsVersion").AsStringValue("", resource.Properties.GetMetadata()), Queues: queues, } + + publicNetworkAccess := resource.Properties.GetMapValue("publicNetworkAccess") + account.PublicNetworkAccess = types.Bool( + publicNetworkAccess.AsStringValue("Enabled", publicNetworkAccess.Metadata).EqualTo("Enabled"), + publicNetworkAccess.Metadata, + ) accounts = append(accounts, account) } return accounts diff --git a/pkg/iac/adapters/arm/storage/adapt_test.go b/pkg/iac/adapters/arm/storage/adapt_test.go index f4fd81f47ad2..372986f23529 100644 --- a/pkg/iac/adapters/arm/storage/adapt_test.go +++ b/pkg/iac/adapters/arm/storage/adapt_test.go @@ -6,17 +6,19 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - azure2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" + "github.com/aquasecurity/trivy/internal/testutil" + "github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage" + "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" "github.com/aquasecurity/trivy/pkg/iac/types" ) func Test_AdaptStorageDefaults(t *testing.T) { - input := azure2.Deployment{ - Resources: []azure2.Resource{ + input := azure.Deployment{ + Resources: []azure.Resource{ { - Type: azure2.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), - Properties: azure2.NewValue(make(map[string]azure2.Value), types.NewTestMetadata()), + Type: azure.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), + Properties: azure.NewValue(make(map[string]azure.Value), types.NewTestMetadata()), }, }, } @@ -28,19 +30,25 @@ func Test_AdaptStorageDefaults(t *testing.T) { account := output.Accounts[0] assert.Equal(t, "", account.MinimumTLSVersion.Value()) assert.False(t, account.EnforceHTTPS.Value()) + assert.True(t, account.PublicNetworkAccess.Value()) } func Test_AdaptStorage(t *testing.T) { - input := azure2.Deployment{ - Resources: []azure2.Resource{ + input := azure.Deployment{ + Resources: []azure.Resource{ { - Type: azure2.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), - Name: azure2.Value{}, - Properties: azure2.NewValue(map[string]azure2.Value{ - "minimumTlsVersion": azure2.NewValue("TLS1_2", types.NewTestMetadata()), - "supportsHttpsTrafficOnly": azure2.NewValue(true, types.NewTestMetadata()), + Type: azure.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), + Name: azure.Value{}, + Properties: azure.NewValue(map[string]azure.Value{ + "minimumTlsVersion": azure.NewValue("TLS1_2", types.NewTestMetadata()), + "supportsHttpsTrafficOnly": azure.NewValue(true, types.NewTestMetadata()), + "publicNetworkAccess": azure.NewValue("Disabled", types.NewTestMetadata()), + "networkAcls": azure.NewValue(map[string]azure.Value{ + "bypass": azure.NewValue("Logging, Metrics", types.NewTestMetadata()), + "defaultAction": azure.NewValue("Allow", types.NewTestMetadata()), + }, types.NewTestMetadata()), }, types.NewTestMetadata()), }, }, @@ -50,8 +58,20 @@ func Test_AdaptStorage(t *testing.T) { require.Len(t, output.Accounts, 1) - account := output.Accounts[0] - assert.Equal(t, "TLS1_2", account.MinimumTLSVersion.Value()) - assert.True(t, account.EnforceHTTPS.Value()) + expected := storage.Storage{ + Accounts: []storage.Account{{ + MinimumTLSVersion: types.StringTest("TLS1_2"), + EnforceHTTPS: types.BoolTest(true), + PublicNetworkAccess: types.BoolTest(false), + NetworkRules: []storage.NetworkRule{{ + Bypass: []types.StringValue{ + types.StringTest("Logging"), + types.StringTest("Metrics"), + }, + AllowByDefault: types.BoolTest(true), + }}, + }}, + } + testutil.AssertDefsecEqual(t, expected, output) } diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go b/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go index b218b95a4e99..c56471aa2bad 100644 --- a/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go +++ b/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go @@ -338,6 +338,34 @@ Resources: }, }, }, + { + name: "empty", + source: `--- +AWSTemplateFormatVersion: 2010-09-09 +Description: Godd example of excessive ports +Resources: + NetworkACL: + Type: AWS::EC2::NetworkAcl + Rule: + Type: AWS::EC2::NetworkAclEntry + Properties: + NetworkAclId: + Ref: NetworkACL`, + expected: ec2.EC2{ + NetworkACLs: []ec2.NetworkACL{ + { + Rules: []ec2.NetworkACLRule{ + { + Action: types.StringTest("allow"), + Type: types.StringTest("ingress"), + FromPort: types.IntTest(-1), + ToPort: types.IntTest(-1), + }, + }, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go b/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go index 65393ae82f82..8d2b0155fee5 100644 --- a/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go +++ b/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go @@ -1,6 +1,7 @@ package apigateway import ( + "net/http" "testing" "github.com/stretchr/testify/assert" @@ -72,7 +73,7 @@ resource "aws_apigatewayv2_domain_name" "example" { { Methods: []v1.Method{ { - HTTPMethod: String("GET"), + HTTPMethod: String(http.MethodGet), AuthorizationType: String("NONE"), APIKeyRequired: Bool(false), }, diff --git a/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go b/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go index cf69e8d0ee23..9932ac864e7b 100644 --- a/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go +++ b/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go @@ -1,6 +1,7 @@ package apigateway import ( + "net/http" "testing" "github.com/aquasecurity/trivy/internal/testutil" @@ -35,7 +36,7 @@ resource "aws_api_gateway_method" "example" { `, expected: []v1.Method{ { - HTTPMethod: String("GET"), + HTTPMethod: String(http.MethodGet), AuthorizationType: String("NONE"), APIKeyRequired: Bool(false), }, @@ -63,7 +64,7 @@ resource "aws_api_gateway_method" "example" { `, expected: []v1.Method{ { - HTTPMethod: String("GET"), + HTTPMethod: String(http.MethodGet), AuthorizationType: String("NONE"), APIKeyRequired: Bool(true), }, diff --git a/pkg/iac/adapters/terraform/azure/storage/adapt.go b/pkg/iac/adapters/terraform/azure/storage/adapt.go index 6a51cf1fca2b..3f125c3c828f 100644 --- a/pkg/iac/adapters/terraform/azure/storage/adapt.go +++ b/pkg/iac/adapters/terraform/azure/storage/adapt.go @@ -108,7 +108,8 @@ func adaptAccount(resource *terraform.Block) storage.Account { Metadata: resource.GetMetadata(), EnableLogging: iacTypes.BoolDefault(false, resource.GetMetadata()), }, - MinimumTLSVersion: iacTypes.StringDefault(minimumTlsVersionOneTwo, resource.GetMetadata()), + MinimumTLSVersion: iacTypes.StringDefault(minimumTlsVersionOneTwo, resource.GetMetadata()), + PublicNetworkAccess: resource.GetAttribute("public_network_access_enabled").AsBoolValueOrDefault(true, resource), } networkRulesBlocks := resource.GetBlocks("network_rules") diff --git a/pkg/iac/adapters/terraform/azure/storage/adapt_test.go b/pkg/iac/adapters/terraform/azure/storage/adapt_test.go index 87ffb1a919f4..5f576731d432 100644 --- a/pkg/iac/adapters/terraform/azure/storage/adapt_test.go +++ b/pkg/iac/adapters/terraform/azure/storage/adapt_test.go @@ -18,6 +18,20 @@ func Test_Adapt(t *testing.T) { terraform string expected storage.Storage }{ + { + name: "default", + terraform: `resource "azurerm_storage_account" "example" {}`, + expected: storage.Storage{ + Accounts: []storage.Account{ + { + PublicNetworkAccess: iacTypes.BoolTest(true), + MinimumTLSVersion: iacTypes.StringTest(minimumTlsVersionOneTwo), + EnforceHTTPS: iacTypes.BoolTest(true), + }, + {}, + }, + }, + }, { name: "defined", terraform: ` @@ -45,6 +59,7 @@ func Test_Adapt(t *testing.T) { } } min_tls_version = "TLS1_2" + public_network_access_enabled = false } resource "azurerm_storage_account_network_rules" "test" { @@ -65,9 +80,10 @@ func Test_Adapt(t *testing.T) { Accounts: []storage.Account{ { - Metadata: iacTypes.NewTestMetadata(), - EnforceHTTPS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - MinimumTLSVersion: iacTypes.String("TLS1_2", iacTypes.NewTestMetadata()), + Metadata: iacTypes.NewTestMetadata(), + EnforceHTTPS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), + MinimumTLSVersion: iacTypes.String("TLS1_2", iacTypes.NewTestMetadata()), + PublicNetworkAccess: iacTypes.BoolTest(false), NetworkRules: []storage.NetworkRule{ { Metadata: iacTypes.NewTestMetadata(), diff --git a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go index 46d1f25fa386..a1a1c792d647 100644 --- a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go +++ b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go @@ -24,7 +24,7 @@ func adaptBuckets(modules terraform.Modules) []spaces.Bucket { Metadata: block.GetMetadata(), Name: block.GetAttribute("name").AsStringValueOrDefault("", block), Objects: nil, - ACL: block.GetAttribute("acl").AsStringValueOrDefault("public-read", block), + ACL: block.GetAttribute("acl").AsStringValueOrDefault("private", block), ForceDestroy: block.GetAttribute("force_destroy").AsBoolValueOrDefault(false, block), Versioning: spaces.Versioning{ Metadata: block.GetMetadata(), diff --git a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go index cf3fc84e4ff7..e87987a3f473 100644 --- a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go +++ b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go @@ -69,7 +69,7 @@ func Test_adaptBuckets(t *testing.T) { Metadata: iacTypes.NewTestMetadata(), Name: iacTypes.String("", iacTypes.NewTestMetadata()), Objects: nil, - ACL: iacTypes.String("public-read", iacTypes.NewTestMetadata()), + ACL: iacTypes.String("private", iacTypes.NewTestMetadata()), ForceDestroy: iacTypes.Bool(false, iacTypes.NewTestMetadata()), Versioning: spaces.Versioning{ Metadata: iacTypes.NewTestMetadata(), diff --git a/pkg/iac/adapters/terraform/google/sql/adapt.go b/pkg/iac/adapters/terraform/google/sql/adapt.go index 6418942384d4..6d68795bcfac 100644 --- a/pkg/iac/adapters/terraform/google/sql/adapt.go +++ b/pkg/iac/adapters/terraform/google/sql/adapt.go @@ -52,6 +52,7 @@ func adaptInstance(resource *terraform.Block) sql.DatabaseInstance { IPConfiguration: sql.IPConfiguration{ Metadata: resource.GetMetadata(), RequireTLS: iacTypes.BoolDefault(false, resource.GetMetadata()), + SSLMode: iacTypes.String("", resource.GetMetadata()), EnableIPv4: iacTypes.BoolDefault(true, resource.GetMetadata()), AuthorizedNetworks: nil, }, @@ -125,12 +126,6 @@ func adaptIPConfig(resource *terraform.Block) sql.IPConfiguration { CIDR iacTypes.StringValue } - tlsRequiredAttr := resource.GetAttribute("require_ssl") - tlsRequiredVal := tlsRequiredAttr.AsBoolValueOrDefault(false, resource) - - ipv4enabledAttr := resource.GetAttribute("ipv4_enabled") - ipv4enabledVal := ipv4enabledAttr.AsBoolValueOrDefault(true, resource) - authNetworksBlocks := resource.GetBlocks("authorized_networks") for _, authBlock := range authNetworksBlocks { nameVal := authBlock.GetAttribute("name").AsStringValueOrDefault("", authBlock) @@ -147,8 +142,9 @@ func adaptIPConfig(resource *terraform.Block) sql.IPConfiguration { return sql.IPConfiguration{ Metadata: resource.GetMetadata(), - RequireTLS: tlsRequiredVal, - EnableIPv4: ipv4enabledVal, + RequireTLS: resource.GetAttribute("require_ssl").AsBoolValueOrDefault(false, resource), + SSLMode: resource.GetAttribute("ssl_mode").AsStringValueOrDefault("", resource), + EnableIPv4: resource.GetAttribute("ipv4_enabled").AsBoolValueOrDefault(true, resource), AuthorizedNetworks: authorizedNetworks, } } diff --git a/pkg/iac/adapters/terraform/google/sql/adapt_test.go b/pkg/iac/adapters/terraform/google/sql/adapt_test.go index 29e89d6282b7..fd3207ed3547 100644 --- a/pkg/iac/adapters/terraform/google/sql/adapt_test.go +++ b/pkg/iac/adapters/terraform/google/sql/adapt_test.go @@ -34,6 +34,7 @@ func Test_Adapt(t *testing.T) { name = "internal" } require_ssl = true + ssl_mode = "TRUSTED_CLIENT_CERTIFICATE_REQUIRED" } } } @@ -67,6 +68,7 @@ func Test_Adapt(t *testing.T) { Metadata: iacTypes.NewTestMetadata(), RequireTLS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), EnableIPv4: iacTypes.Bool(false, iacTypes.NewTestMetadata()), + SSLMode: iacTypes.StringTest("TRUSTED_CLIENT_CERTIFICATE_REQUIRED"), AuthorizedNetworks: []struct { Name iacTypes.StringValue CIDR iacTypes.StringValue diff --git a/pkg/iac/providers/azure/storage/storage.go b/pkg/iac/providers/azure/storage/storage.go index cccc5d55eda1..ce86ec8698cc 100755 --- a/pkg/iac/providers/azure/storage/storage.go +++ b/pkg/iac/providers/azure/storage/storage.go @@ -9,13 +9,14 @@ type Storage struct { } type Account struct { - Metadata iacTypes.Metadata - NetworkRules []NetworkRule - EnforceHTTPS iacTypes.BoolValue - Containers []Container - QueueProperties QueueProperties - MinimumTLSVersion iacTypes.StringValue - Queues []Queue + Metadata iacTypes.Metadata + NetworkRules []NetworkRule + EnforceHTTPS iacTypes.BoolValue + Containers []Container + QueueProperties QueueProperties + MinimumTLSVersion iacTypes.StringValue + Queues []Queue + PublicNetworkAccess iacTypes.BoolValue } type Queue struct { diff --git a/pkg/iac/providers/google/sql/sql.go b/pkg/iac/providers/google/sql/sql.go index 18778dd1daef..672e78a42fbd 100755 --- a/pkg/iac/providers/google/sql/sql.go +++ b/pkg/iac/providers/google/sql/sql.go @@ -66,6 +66,7 @@ type Backups struct { type IPConfiguration struct { Metadata iacTypes.Metadata RequireTLS iacTypes.BoolValue + SSLMode iacTypes.StringValue EnableIPv4 iacTypes.BoolValue AuthorizedNetworks []struct { Name iacTypes.StringValue diff --git a/pkg/iac/rego/convert/slice_test.go b/pkg/iac/rego/convert/slice_test.go index e30c200142b2..ff318e2b1a03 100644 --- a/pkg/iac/rego/convert/slice_test.go +++ b/pkg/iac/rego/convert/slice_test.go @@ -38,6 +38,7 @@ func Test_SliceTypesConversion(t *testing.T) { "endline": 123, "sourceprefix": "", "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", "resource": "", @@ -49,6 +50,7 @@ func Test_SliceTypesConversion(t *testing.T) { "endline": 123, "sourceprefix": "", "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", "resource": "", diff --git a/pkg/iac/rego/exceptions.go b/pkg/iac/rego/exceptions.go deleted file mode 100644 index 591c72abdbbb..000000000000 --- a/pkg/iac/rego/exceptions.go +++ /dev/null @@ -1,35 +0,0 @@ -package rego - -import ( - "context" - "fmt" - - "github.com/open-policy-agent/opa/ast" -) - -func (s *Scanner) isIgnored(ctx context.Context, namespace, ruleName string, input ast.Value) (bool, error) { - if ignored, err := s.isNamespaceIgnored(ctx, namespace, input); err != nil { - return false, err - } else if ignored { - return true, nil - } - return s.isRuleIgnored(ctx, namespace, ruleName, input) -} - -func (s *Scanner) isNamespaceIgnored(ctx context.Context, namespace string, input ast.Value) (bool, error) { - exceptionQuery := fmt.Sprintf("data.namespace.exceptions.exception[_] == %q", namespace) - result, _, err := s.runQuery(ctx, exceptionQuery, input, true) - if err != nil { - return false, fmt.Errorf("query namespace exceptions: %w", err) - } - return result.Allowed(), nil -} - -func (s *Scanner) isRuleIgnored(ctx context.Context, namespace, ruleName string, input ast.Value) (bool, error) { - exceptionQuery := fmt.Sprintf("endswith(%q, data.%s.exception[_][_])", ruleName, namespace) - result, _, err := s.runQuery(ctx, exceptionQuery, input, true) - if err != nil { - return false, err - } - return result.Allowed(), nil -} diff --git a/pkg/iac/rego/scanner.go b/pkg/iac/rego/scanner.go index 9cf80fc83da0..f791a45bf74d 100644 --- a/pkg/iac/rego/scanner.go +++ b/pkg/iac/rego/scanner.go @@ -345,16 +345,7 @@ func (s *Scanner) applyRule(ctx context.Context, namespace, rule string, inputs s.logger.Error("Error occurred while parsing input", log.Err(err)) continue } - if ignored, err := s.isIgnored(ctx, namespace, rule, parsedInput); err != nil { - return nil, err - } else if ignored { - var result regoResult - result.FS = input.FS - result.Filepath = input.Path - result.Managed = true - results.AddIgnored(result) - continue - } + set, traces, err := s.runQuery(ctx, qualified, parsedInput, false) if err != nil { return nil, err @@ -385,20 +376,6 @@ func (s *Scanner) applyRuleCombined(ctx context.Context, namespace, rule string, return nil, fmt.Errorf("failed to parse input: %w", err) } - var results scan.Results - - if ignored, err := s.isIgnored(ctx, namespace, rule, parsed); err != nil { - return nil, err - } else if ignored { - for _, input := range inputs { - var result regoResult - result.FS = input.FS - result.Filepath = input.Path - result.Managed = true - results.AddIgnored(result) - } - return results, nil - } qualified := fmt.Sprintf("data.%s.%s", namespace, rule) set, traces, err := s.runQuery(ctx, qualified, parsed, false) if err != nil { diff --git a/pkg/iac/rego/scanner_test.go b/pkg/iac/rego/scanner_test.go index 1310e56d2ec4..880bc9c1f9f9 100644 --- a/pkg/iac/rego/scanner_test.go +++ b/pkg/iac/rego/scanner_test.go @@ -166,168 +166,6 @@ deny { assert.Equal(t, "/evil.lol", results.GetPassed()[0].Metadata().Range().GetFilename()) } -func Test_RegoScanning_Namespace_Exception(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, - "policies/exceptions.rego": ` -package namespace.exceptions - -import data.namespaces - -exception[ns] { - ns := data.namespaces[_] - startswith(ns, "defsec") -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Empty(t, results.GetFailed()) - assert.Empty(t, results.GetPassed()) - assert.Len(t, results.GetIgnored(), 1) - -} - -func Test_RegoScanning_Namespace_Exception_WithoutMatch(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, "policies/something.rego": ` -package builtin.test - -deny_something { - input.something -} -`, - "policies/exceptions.rego": ` -package namespace.exceptions - -import data.namespaces - -exception[ns] { - ns := data.namespaces[_] - startswith(ns, "builtin") -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Len(t, results.GetIgnored(), 1) - -} - -func Test_RegoScanning_Rule_Exception(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test -deny_evil { - input.evil -} -`, - "policies/exceptions.rego": ` -package defsec.test - -exception[rules] { - rules := ["evil"] -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Empty(t, results.GetFailed()) - assert.Empty(t, results.GetPassed()) - assert.Len(t, results.GetIgnored(), 1) -} - -func Test_RegoScanning_Rule_Exception_WithoutMatch(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test -deny_evil { - input.evil -} -`, - "policies/exceptions.rego": ` -package defsec.test - -exception[rules] { - rules := ["good"] -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) -} - func Test_RegoScanning_WithRuntimeValues(t *testing.T) { t.Setenv("DEFSEC_RUNTIME_VAL", "AOK") diff --git a/pkg/iac/rego/schemas/cloud.json b/pkg/iac/rego/schemas/cloud.json index b034f24fa104..bdaad1330898 100644 --- a/pkg/iac/rego/schemas/cloud.json +++ b/pkg/iac/rego/schemas/cloud.json @@ -5396,6 +5396,10 @@ "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.NetworkRule" } }, + "publicnetworkaccess": { + "type": "object", + "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" + }, "queueproperties": { "type": "object", "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.storage.QueueProperties" @@ -6987,6 +6991,10 @@ "requiretls": { "type": "object", "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue" + }, + "sslmode": { + "type": "object", + "$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue" } } }, @@ -7876,6 +7884,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "boolean" } @@ -7908,6 +7919,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "string" } @@ -7940,6 +7954,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "integer" } @@ -7972,6 +7989,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "object" } @@ -8003,6 +8023,9 @@ }, "startline": { "type": "integer" + }, + "unresolvable": { + "type": "boolean" } } }, @@ -8033,6 +8056,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "string" } @@ -8065,6 +8091,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "string" } diff --git a/pkg/iac/scanners/cloudformation/parser/parser.go b/pkg/iac/scanners/cloudformation/parser/parser.go index 40d0035f6cd2..696dfcf16349 100644 --- a/pkg/iac/scanners/cloudformation/parser/parser.go +++ b/pkg/iac/scanners/cloudformation/parser/parser.go @@ -3,13 +3,13 @@ package parser import ( "context" "encoding/json" - "errors" "fmt" "io" "io/fs" "path/filepath" "strings" + "github.com/hashicorp/go-multierror" "github.com/liamg/jfather" "gopkg.in/yaml.v3" @@ -171,18 +171,17 @@ func (p *Parser) parseParams() error { params := make(Parameters) - var errs []error - + var errs error for _, path := range p.parameterFiles { if parameters, err := p.parseParametersFile(path); err != nil { - errs = append(errs, err) + errs = multierror.Append(errs, err) } else { params.Merge(parameters) } } - if len(errs) != 0 { - return errors.Join(errs...) + if errs != nil { + return errs } params.Merge(p.parameters) diff --git a/pkg/iac/scanners/cloudformation/parser/property_conversion.go b/pkg/iac/scanners/cloudformation/parser/property_conversion.go index 35847bb35c5b..d1ef23d396f2 100644 --- a/pkg/iac/scanners/cloudformation/parser/property_conversion.go +++ b/pkg/iac/scanners/cloudformation/parser/property_conversion.go @@ -10,6 +10,10 @@ import ( ) func (p *Property) IsConvertableTo(conversionType cftypes.CfType) bool { + if p.IsNil() { + return false + } + switch conversionType { case cftypes.Int: return p.isConvertableToInt() @@ -62,6 +66,9 @@ func (p *Property) isConvertableToInt() bool { } func (p *Property) ConvertTo(conversionType cftypes.CfType) *Property { + if p.IsNil() { + return nil + } if p.Type() == conversionType { return p diff --git a/pkg/iac/scanners/cloudformation/scanner.go b/pkg/iac/scanners/cloudformation/scanner.go index 4497a807506c..0e3fec472b96 100644 --- a/pkg/iac/scanners/cloudformation/scanner.go +++ b/pkg/iac/scanners/cloudformation/scanner.go @@ -205,6 +205,7 @@ func (s *Scanner) scanFileContext(ctx context.Context, regoScanner *rego.Scanner } results = append(results, regoResults...) + // ignore a result based on user input results.Ignore(cfCtx.Ignores, nil) for _, ignored := range results.GetIgnored() { diff --git a/pkg/iac/scanners/dockerfile/parser/parser.go b/pkg/iac/scanners/dockerfile/parser/parser.go index e92116ae3079..128d9515e8b1 100644 --- a/pkg/iac/scanners/dockerfile/parser/parser.go +++ b/pkg/iac/scanners/dockerfile/parser/parser.go @@ -4,76 +4,26 @@ import ( "context" "fmt" "io" - "io/fs" - "path/filepath" "strings" "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile" - "github.com/aquasecurity/trivy/pkg/log" ) -type Parser struct { - logger *log.Logger -} - -// New creates a new Dockerfile parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("dockerfile parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string]*dockerfile.Dockerfile, error) { - - files := make(map[string]*dockerfile.Dockerfile) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Failed to parse Dockerfile", log.FilePath(path), log.Err(err)) - return nil - } - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses Dockerfile content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) (*dockerfile.Dockerfile, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - return p.parse(path, f) -} - -func (p *Parser) parse(path string, r io.Reader) (*dockerfile.Dockerfile, error) { +func Parse(_ context.Context, r io.Reader, path string) (any, error) { parsed, err := parser.Parse(r) if err != nil { return nil, fmt.Errorf("dockerfile parse error: %w", err) } - var parsedFile dockerfile.Dockerfile - var stage dockerfile.Stage - var stageIndex int + var ( + parsedFile dockerfile.Dockerfile + stage dockerfile.Stage + stageIndex int + ) + fromValue := "args" for _, child := range parsed.AST.Children { child.Value = strings.ToLower(child.Value) diff --git a/pkg/iac/scanners/dockerfile/parser/parser_test.go b/pkg/iac/scanners/dockerfile/parser/parser_test.go index 764cb3a0e091..ad1acf193bf3 100644 --- a/pkg/iac/scanners/dockerfile/parser/parser_test.go +++ b/pkg/iac/scanners/dockerfile/parser/parser_test.go @@ -1,11 +1,15 @@ -package parser +package parser_test import ( + "context" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile" + "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile/parser" ) func Test_Parser(t *testing.T) { @@ -15,12 +19,13 @@ RUN make /app CMD python /app/app.py ` - df, err := New().parse("Dockerfile", strings.NewReader(input)) + res, err := parser.Parse(context.TODO(), strings.NewReader(input), "Dockerfile") require.NoError(t, err) - assert.Len(t, df.Stages, 1) + df, ok := res.(*dockerfile.Dockerfile) + require.True(t, ok) - require.Len(t, df.Stages, 1) + assert.Len(t, df.Stages, 1) assert.Equal(t, "ubuntu:18.04", df.Stages[0].Name) commands := df.Stages[0].Commands diff --git a/pkg/iac/scanners/dockerfile/scanner.go b/pkg/iac/scanners/dockerfile/scanner.go index 1694f28d814a..5637410945eb 100644 --- a/pkg/iac/scanners/dockerfile/scanner.go +++ b/pkg/iac/scanners/dockerfile/scanner.go @@ -1,114 +1,12 @@ package dockerfile import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile/parser" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" "github.com/aquasecurity/trivy/pkg/iac/scanners/options" "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" ) -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - parser *parser.Parser - regoScanner *rego.Scanner - options []options.ScannerOption -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func (s *Scanner) Name() string { - return "Dockerfile" -} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("dockerfile scanner"), - } - for _, opt := range opts { - opt(s) - } - s.parser = parser.New() - return s -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - files, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(files) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, dfile := range files { - inputs = append(inputs, rego.Input{ - Path: path, - FS: fsys, - Contents: dfile.ToRego(), - }) - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - dockerfile, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.FilePath(path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: dockerfile.ToRego(), - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - - regoScanner := rego.NewScanner(types.SourceDockerfile, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil +func NewScanner(opts ...options.ScannerOption) *generic.GenericScanner { + return generic.NewScanner("Dockerfile", types.SourceDockerfile, generic.ParseFunc(parser.Parse), opts...) } diff --git a/pkg/iac/scanners/dockerfile/scanner_test.go b/pkg/iac/scanners/dockerfile/scanner_test.go index 2872df1e4ef6..e8c66b27db08 100644 --- a/pkg/iac/scanners/dockerfile/scanner_test.go +++ b/pkg/iac/scanners/dockerfile/scanner_test.go @@ -1,4 +1,4 @@ -package dockerfile +package dockerfile_test import ( "bytes" @@ -13,6 +13,7 @@ import ( "github.com/aquasecurity/trivy/pkg/iac/rego" "github.com/aquasecurity/trivy/pkg/iac/rego/schemas" "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile" ) const DS006PolicyWithDockerfileSchema = `# METADATA @@ -219,7 +220,7 @@ USER root "/rules/rule.rego": DS006LegacyWithOldStyleMetadata, }) - scanner := NewScanner(rego.WithPolicyDirs("rules")) + scanner := dockerfile.NewScanner(rego.WithPolicyDirs("rules")) results, err := scanner.ScanFS(context.TODO(), fs, "code") require.NoError(t, err) @@ -563,7 +564,7 @@ COPY --from=dep /binary /` var traceBuf bytes.Buffer - scanner := NewScanner( + scanner := dockerfile.NewScanner( rego.WithPolicyDirs("rules"), rego.WithEmbeddedLibraries(true), rego.WithTrace(&traceBuf), diff --git a/pkg/iac/scanners/generic/scanner.go b/pkg/iac/scanners/generic/scanner.go new file mode 100644 index 000000000000..5a36709d9a04 --- /dev/null +++ b/pkg/iac/scanners/generic/scanner.go @@ -0,0 +1,220 @@ +package generic + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/fs" + "path/filepath" + "strings" + "sync" + + "github.com/BurntSushi/toml" + "gopkg.in/yaml.v3" + + "github.com/aquasecurity/trivy/pkg/iac/framework" + "github.com/aquasecurity/trivy/pkg/iac/rego" + "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/options" + "github.com/aquasecurity/trivy/pkg/iac/types" + "github.com/aquasecurity/trivy/pkg/log" +) + +func NewJsonScanner(opts ...options.ScannerOption) *GenericScanner { + return NewScanner("JSON", types.SourceJSON, ParseFunc(parseJson), opts...) +} + +func NewYamlScanner(opts ...options.ScannerOption) *GenericScanner { + return NewScanner("YAML", types.SourceYAML, ParseFunc(parseYaml), opts...) +} + +func NewTomlScanner(opts ...options.ScannerOption) *GenericScanner { + return NewScanner("TOML", types.SourceTOML, ParseFunc(parseTOML), opts...) +} + +type configParser interface { + Parse(ctx context.Context, r io.Reader, path string) (any, error) +} + +// GenericScanner is a scanner that scans a file as is without processing it +type GenericScanner struct { + mu sync.Mutex + name string + source types.Source + logger *log.Logger + options []options.ScannerOption + regoScanner *rego.Scanner + + parser configParser +} + +type ParseFunc func(ctx context.Context, r io.Reader, path string) (any, error) + +func (f ParseFunc) Parse(ctx context.Context, r io.Reader, path string) (any, error) { + return f(ctx, r, path) +} + +func NewScanner(name string, source types.Source, parser configParser, opts ...options.ScannerOption) *GenericScanner { + s := &GenericScanner{ + name: name, + options: opts, + source: source, + logger: log.WithPrefix(fmt.Sprintf("%s scanner", source)), + parser: parser, + } + + for _, opt := range opts { + opt(s) + } + + return s +} + +func (s *GenericScanner) Name() string { + return s.name +} + +func (s *GenericScanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (scan.Results, error) { + fileset, err := s.parseFS(ctx, fsys, dir) + if err != nil { + return nil, err + } + + if len(fileset) == 0 { + return nil, nil + } + + var inputs []rego.Input + for path, val := range fileset { + switch v := val.(type) { + case interface{ ToRego() any }: + inputs = append(inputs, rego.Input{ + Path: path, + Contents: v.ToRego(), + FS: fsys, + }) + case []any: + for _, file := range v { + inputs = append(inputs, rego.Input{ + Path: path, + Contents: file, + FS: fsys, + }) + } + default: + inputs = append(inputs, rego.Input{ + Path: path, + Contents: v, + FS: fsys, + }) + } + } + + regoScanner, err := s.initRegoScanner(fsys) + if err != nil { + return nil, err + } + + s.logger.Debug("Scanning files...", log.Int("count", len(inputs))) + results, err := regoScanner.ScanInput(ctx, inputs...) + if err != nil { + return nil, err + } + results.SetSourceAndFilesystem("", fsys, false) + return results, nil +} + +func (s *GenericScanner) parseFS(ctx context.Context, fsys fs.FS, path string) (map[string]any, error) { + files := make(map[string]any) + if err := fs.WalkDir(fsys, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + if err != nil { + return err + } + if entry.IsDir() { + return nil + } + + f, err := fsys.Open(filepath.ToSlash(path)) + if err != nil { + return err + } + defer f.Close() + + df, err := s.parser.Parse(ctx, f, path) + if err != nil { + s.logger.Error("Failed to parse file", log.FilePath(path), log.Err(err)) + return nil + } + files[path] = df + return nil + }); err != nil { + return nil, err + } + return files, nil +} + +func (s *GenericScanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.regoScanner != nil { + return s.regoScanner, nil + } + regoScanner := rego.NewScanner(s.source, s.options...) + if err := regoScanner.LoadPolicies(srcFS); err != nil { + return nil, err + } + s.regoScanner = regoScanner + return regoScanner, nil +} + +func (*GenericScanner) SetRegoOnly(bool) {} +func (*GenericScanner) SetIncludeDeprecatedChecks(bool) {} +func (*GenericScanner) SetFrameworks([]framework.Framework) {} + +func parseJson(ctx context.Context, r io.Reader, _ string) (any, error) { + var target any + if err := json.NewDecoder(r).Decode(&target); err != nil { + return nil, err + } + return target, nil +} + +func parseYaml(ctx context.Context, r io.Reader, _ string) (any, error) { + contents, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + var results []any + + marker := "\n---\n" + altMarker := "\r\n---\r\n" + if bytes.Contains(contents, []byte(altMarker)) { + marker = altMarker + } + + for _, partial := range strings.Split(string(contents), marker) { + var target any + if err := yaml.Unmarshal([]byte(partial), &target); err != nil { + return nil, err + } + results = append(results, target) + } + + return results, nil +} + +func parseTOML(ctx context.Context, r io.Reader, _ string) (any, error) { + var target any + if _, err := toml.NewDecoder(r).Decode(&target); err != nil { + return nil, err + } + return target, nil +} diff --git a/pkg/iac/scanners/generic/scanner_test.go b/pkg/iac/scanners/generic/scanner_test.go new file mode 100644 index 000000000000..46d0eef1653a --- /dev/null +++ b/pkg/iac/scanners/generic/scanner_test.go @@ -0,0 +1,223 @@ +package generic_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/internal/testutil" + "github.com/aquasecurity/trivy/pkg/iac/framework" + "github.com/aquasecurity/trivy/pkg/iac/rego" + "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" +) + +func TestJsonScanner(t *testing.T) { + fsys := testutil.CreateFS(t, map[string]string{ + "/code/data.json": `{ "x": { "y": 123, "z": ["a", "b", "c"]}}`, + "/rules/rule.rego": `package builtin.json.lol + +__rego_metadata__ := { + "id": "ABC123", + "avd_id": "AVD-AB-0123", + "title": "title", + "short_code": "short", + "severity": "CRITICAL", + "type": "JSON Check", + "description": "description", + "recommended_actions": "actions", + "url": "https://example.com", +} + +__rego_input__ := { + "combine": false, + "selector": [{"type": "json"}], +} + +deny[res] { + input.x.y == 123 + res := { + "msg": "oh no", + "startline": 1, + "endline": 2, + } +} + +`, + }) + + scanner := generic.NewJsonScanner(rego.WithPolicyDirs("rules")) + + results, err := scanner.ScanFS(context.TODO(), fsys, "code") + require.NoError(t, err) + + require.Len(t, results.GetFailed(), 1) + + assert.Equal(t, scan.Rule{ + AVDID: "AVD-AB-0123", + Aliases: []string{"ABC123"}, + ShortCode: "short", + Summary: "title", + Explanation: "description", + Impact: "", + Resolution: "actions", + Provider: "json", + Service: "general", + Links: []string{"https://example.com"}, + Severity: "CRITICAL", + Terraform: &scan.EngineMetadata{}, + CloudFormation: &scan.EngineMetadata{}, + CustomChecks: scan.CustomChecks{ + Terraform: (*scan.TerraformCustomCheck)(nil), + }, + RegoPackage: "data.builtin.json.lol", + Frameworks: map[framework.Framework][]string{ + framework.Default: {}, + }, + }, results.GetFailed()[0].Rule()) +} + +func TestYamlScanner(t *testing.T) { + fsys := testutil.CreateFS(t, map[string]string{ + "/code/data.yaml": `--- +x: + y: 123 + z: + - a + - b + - c +`, + "/rules/rule.rego": `package builtin.yaml.lol + +__rego_metadata__ := { + "id": "ABC123", + "avd_id": "AVD-AB-0123", + "title": "title", + "short_code": "short", + "severity": "CRITICAL", + "type": "YAML Check", + "description": "description", + "recommended_actions": "actions", + "url": "https://example.com", +} + +__rego_input__ := { + "combine": false, + "selector": [{"type": "yaml"}], +} + +deny[res] { + input.x.y == 123 + res := { + "msg": "oh no", + "startline": 1, + "endline": 2, + } +} + +`, + }) + + scanner := generic.NewYamlScanner(rego.WithPolicyDirs("rules")) + + results, err := scanner.ScanFS(context.TODO(), fsys, "code") + require.NoError(t, err) + + require.Len(t, results.GetFailed(), 1) + + assert.Equal(t, scan.Rule{ + AVDID: "AVD-AB-0123", + Aliases: []string{"ABC123"}, + ShortCode: "short", + Summary: "title", + Explanation: "description", + Impact: "", + Resolution: "actions", + Provider: "yaml", + Service: "general", + Links: []string{"https://example.com"}, + Severity: "CRITICAL", + Terraform: &scan.EngineMetadata{}, + CloudFormation: &scan.EngineMetadata{}, + CustomChecks: scan.CustomChecks{ + Terraform: (*scan.TerraformCustomCheck)(nil)}, + RegoPackage: "data.builtin.yaml.lol", + Frameworks: map[framework.Framework][]string{ + framework.Default: {}, + }, + }, + results.GetFailed()[0].Rule(), + ) +} + +func TestTomlParser(t *testing.T) { + fsys := testutil.CreateFS(t, map[string]string{ + "/code/code.toml": ` +[x] +y = 123 +z = ["a", "b", "c"] +`, + "/rules/rule.rego": `package builtin.toml.lol + +__rego_metadata__ := { + "id": "ABC123", + "avd_id": "AVD-AB-0123", + "title": "title", + "short_code": "short", + "severity": "CRITICAL", + "type": "TOML Check", + "description": "description", + "recommended_actions": "actions", + "url": "https://example.com", +} + +__rego_input__ := { + "combine": false, + "selector": [{"type": "toml"}], +} + +deny[res] { + input.x.y == 123 + res := { + "msg": "oh no", + "startline": 1, + "endline": 2, + } +} + +`, + }) + + scanner := generic.NewTomlScanner(rego.WithPolicyDirs("rules")) + + results, err := scanner.ScanFS(context.TODO(), fsys, "code") + require.NoError(t, err) + + require.Len(t, results.GetFailed(), 1) + + assert.Equal(t, scan.Rule{ + AVDID: "AVD-AB-0123", + Aliases: []string{"ABC123"}, + ShortCode: "short", + Summary: "title", + Explanation: "description", + Impact: "", + Resolution: "actions", + Provider: "toml", + Service: "general", + Links: []string{"https://example.com"}, + Severity: "CRITICAL", + Terraform: &scan.EngineMetadata{}, + CloudFormation: &scan.EngineMetadata{}, + CustomChecks: scan.CustomChecks{ + Terraform: (*scan.TerraformCustomCheck)(nil)}, + RegoPackage: "data.builtin.toml.lol", + Frameworks: map[framework.Framework][]string{ + framework.Default: {}, + }, + }, + results.GetFailed()[0].Rule(), + ) +} diff --git a/pkg/iac/scanners/helm/parser/parser.go b/pkg/iac/scanners/helm/parser/parser.go index e38f032b6221..1a132fdc2f76 100644 --- a/pkg/iac/scanners/helm/parser/parser.go +++ b/pkg/iac/scanners/helm/parser/parser.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io/fs" + "path" "path/filepath" "regexp" "sort" @@ -46,7 +47,7 @@ type ChartFile struct { ManifestContent string } -func New(path string, opts ...Option) (*Parser, error) { +func New(src string, opts ...Option) (*Parser, error) { client := action.NewInstall(&action.Configuration{}) client.DryRun = true // don't do anything @@ -55,7 +56,7 @@ func New(path string, opts ...Option) (*Parser, error) { p := &Parser{ helmClient: client, - ChartSource: path, + ChartSource: src, logger: log.WithPrefix("helm parser"), } @@ -79,10 +80,10 @@ func New(path string, opts ...Option) (*Parser, error) { return p, nil } -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { - p.workingFS = target +func (p *Parser) ParseFS(ctx context.Context, fsys fs.FS, target string) error { + p.workingFS = fsys - if err := fs.WalkDir(p.workingFS, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { + if err := fs.WalkDir(p.workingFS, filepath.ToSlash(target), func(filePath string, entry fs.DirEntry, err error) error { select { case <-ctx.Done(): return ctx.Err() @@ -95,16 +96,20 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { return nil } - if detection.IsArchive(path) { - tarFS, err := p.addTarToFS(path) + if _, err := fs.Stat(p.workingFS, filePath); err != nil { + return nil + } + + if detection.IsArchive(filePath) && !isDependencyChartArchive(p.workingFS, filePath) { + tarFS, err := p.addTarToFS(filePath) if errors.Is(err, errSkipFS) { // an unpacked Chart already exists return nil } else if err != nil { - return fmt.Errorf("failed to add tar %q to FS: %w", path, err) + return fmt.Errorf("failed to add tar %q to FS: %w", filePath, err) } - targetPath := filepath.Dir(path) + targetPath := filepath.Dir(filePath) if targetPath == "" { targetPath = "." } @@ -114,7 +119,7 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { } return nil } else { - return p.addPaths(path) + return p.addPaths(filePath) } }); err != nil { return fmt.Errorf("walk dir error: %w", err) @@ -123,19 +128,29 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { return nil } +func isDependencyChartArchive(fsys fs.FS, archivePath string) bool { + parent := path.Dir(archivePath) + if path.Base(parent) != "charts" { + return false + } + + _, err := fs.Stat(fsys, path.Join(parent, "..", "Chart.yaml")) + return err == nil +} + func (p *Parser) addPaths(paths ...string) error { - for _, path := range paths { - if _, err := fs.Stat(p.workingFS, path); err != nil { + for _, filePath := range paths { + if _, err := fs.Stat(p.workingFS, filePath); err != nil { return err } - if strings.HasSuffix(path, "Chart.yaml") && p.rootPath == "" { - if err := p.extractChartName(path); err != nil { + if strings.HasSuffix(filePath, "Chart.yaml") && p.rootPath == "" { + if err := p.extractChartName(filePath); err != nil { return err } - p.rootPath = filepath.Dir(path) + p.rootPath = filepath.Dir(filePath) } - p.filepaths = append(p.filepaths, path) + p.filepaths = append(p.filepaths, filePath) } return nil } diff --git a/pkg/iac/scanners/helm/parser/parser_test.go b/pkg/iac/scanners/helm/parser/parser_test.go index 9c8b05ce7696..01aa697d559c 100644 --- a/pkg/iac/scanners/helm/parser/parser_test.go +++ b/pkg/iac/scanners/helm/parser/parser_test.go @@ -48,4 +48,34 @@ func TestParseFS(t *testing.T) { } assert.Equal(t, expectedFiles, p.filepaths) }) + + t.Run("chart with multiple archived deps", func(t *testing.T) { + p, err := New(".") + require.NoError(t, err) + + fsys := os.DirFS(filepath.Join("testdata", "multiple-archived-deps")) + require.NoError(t, p.ParseFS(context.TODO(), fsys, ".")) + + expectedFiles := []string{ + "Chart.yaml", + "charts/common-2.26.0.tgz", + "charts/opentelemetry-collector-0.108.0.tgz", + } + assert.Equal(t, expectedFiles, p.filepaths) + }) + + t.Run("archives are not dependencies", func(t *testing.T) { + p, err := New(".") + require.NoError(t, err) + + fsys := os.DirFS(filepath.Join("testdata", "non-deps-archives")) + require.NoError(t, p.ParseFS(context.TODO(), fsys, ".")) + + expectedFiles := []string{ + "Chart.yaml", + "backup_charts/wordpress-operator/Chart.yaml", + "backup_charts/mysql-operator/Chart.yaml", + } + assert.Subset(t, p.filepaths, expectedFiles) + }) } diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml new file mode 100644 index 000000000000..82d6c918088f --- /dev/null +++ b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +appVersion: "1.1" +description: Test Chart +name: y-chart +version: 1.0.0 +kubeVersion: ">=1.21" + +dependencies: + - name: common + repository: https://charts.bitnami.com/bitnami + version: 2.26.0 + - name: opentelemetry-collector + version: 0.108.0 + repository: https://open-telemetry.github.io/opentelemetry-helm-charts \ No newline at end of file diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz new file mode 100644 index 000000000000..43f85ba36a91 Binary files /dev/null and b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz differ diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/opentelemetry-collector-0.108.0.tgz b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/opentelemetry-collector-0.108.0.tgz new file mode 100644 index 000000000000..0ea88fb48247 Binary files /dev/null and b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/opentelemetry-collector-0.108.0.tgz differ diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml new file mode 100644 index 000000000000..65225b732935 --- /dev/null +++ b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +appVersion: "1.1" +description: Test Chart +name: y-chart +version: 1.0.0 +kubeVersion: ">=1.21" + +dependencies: + - name: mysql-operator + repository: https://mysql.github.io/mysql-operator/ + version: 2.2.2 + - name: wordpress-operator + version: 0.12.4 + repository: https://helm-charts.bitpoke.io \ No newline at end of file diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz new file mode 100644 index 000000000000..b80fc3c0425e Binary files /dev/null and b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz differ diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz new file mode 100644 index 000000000000..4086e9f1a6ba Binary files /dev/null and b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz differ diff --git a/pkg/iac/scanners/helm/scanner.go b/pkg/iac/scanners/helm/scanner.go index bfa63a5d2c00..daf8f3108628 100644 --- a/pkg/iac/scanners/helm/scanner.go +++ b/pkg/iac/scanners/helm/scanner.go @@ -130,7 +130,7 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS) file := file s.logger.Debug("Processing rendered chart file", log.FilePath(file.TemplateFilePath)) - manifests, err := kparser.New().Parse(strings.NewReader(file.ManifestContent), file.TemplateFilePath) + manifests, err := kparser.Parse(ctx, strings.NewReader(file.ManifestContent), file.TemplateFilePath) if err != nil { return nil, fmt.Errorf("unmarshal yaml: %w", err) } diff --git a/pkg/iac/scanners/json/parser/parser.go b/pkg/iac/scanners/json/parser/parser.go deleted file mode 100644 index c97e54b40bd9..000000000000 --- a/pkg/iac/scanners/json/parser/parser.go +++ /dev/null @@ -1,65 +0,0 @@ -package parser - -import ( - "context" - "encoding/json" - "io/fs" - "path/filepath" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger -} - -// New creates a new parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("json parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string]any, error) { - - files := make(map[string]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses Dockerfile content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) (any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - var target any - if err := json.NewDecoder(f).Decode(&target); err != nil { - return nil, err - } - return target, nil -} diff --git a/pkg/iac/scanners/json/parser/parser_test.go b/pkg/iac/scanners/json/parser/parser_test.go deleted file mode 100644 index a47868fad0ed..000000000000 --- a/pkg/iac/scanners/json/parser/parser_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package parser - -import ( - "context" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Parser(t *testing.T) { - input := `{ "x": { "y": 123, "z": ["a", "b", "c"]}}` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.json", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.json") - require.NoError(t, err) - - msi, ok := data.(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(float64) - require.True(t, ok) - - assert.InEpsilon(t, 123.0, y, 0.0001) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - -} diff --git a/pkg/iac/scanners/json/scanner.go b/pkg/iac/scanners/json/scanner.go deleted file mode 100644 index f46f9cc56bc9..000000000000 --- a/pkg/iac/scanners/json/scanner.go +++ /dev/null @@ -1,113 +0,0 @@ -package json - -import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" - "github.com/aquasecurity/trivy/pkg/iac/scanners/json/parser" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - parser *parser.Parser - regoScanner *rego.Scanner - options []options.ScannerOption -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("json scanner"), - parser: parser.New(), - } - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) Name() string { - return "JSON" -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - files, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(files) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, file := range files { - inputs = append(inputs, rego.Input{ - Path: path, - FS: fsys, - Contents: file, - }) - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - parsed, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.FilePath(path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: parsed, - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceJSON, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil -} diff --git a/pkg/iac/scanners/json/scanner_test.go b/pkg/iac/scanners/json/scanner_test.go deleted file mode 100644 index 6656f70b2e25..000000000000 --- a/pkg/iac/scanners/json/scanner_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package json - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_BasicScan(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/data.json": `{ "x": { "y": 123, "z": ["a", "b", "c"]}}`, - "/rules/rule.rego": `package builtin.json.lol - -__rego_metadata__ := { - "id": "ABC123", - "avd_id": "AVD-AB-0123", - "title": "title", - "short_code": "short", - "severity": "CRITICAL", - "type": "JSON Check", - "description": "description", - "recommended_actions": "actions", - "url": "https://example.com", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "json"}], -} - -deny[res] { - input.x.y == 123 - res := { - "msg": "oh no", - "startline": 1, - "endline": 2, - } -} - -`, - }) - - scanner := NewScanner(rego.WithPolicyDirs("rules")) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-AB-0123", - Aliases: []string{"ABC123"}, - ShortCode: "short", - Summary: "title", - Explanation: "description", - Impact: "", - Resolution: "actions", - Provider: "json", - Service: "general", - Links: []string{"https://example.com"}, - Severity: "CRITICAL", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{ - Terraform: (*scan.TerraformCustomCheck)(nil), - }, - RegoPackage: "data.builtin.json.lol", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, results.GetFailed()[0].Rule()) -} diff --git a/pkg/iac/scanners/kubernetes/parser/parser.go b/pkg/iac/scanners/kubernetes/parser/parser.go index f3ca7d613562..57d723801c98 100644 --- a/pkg/iac/scanners/kubernetes/parser/parser.go +++ b/pkg/iac/scanners/kubernetes/parser/parser.go @@ -5,69 +5,14 @@ import ( "encoding/json" "fmt" "io" - "io/fs" - "path/filepath" "regexp" "strings" "gopkg.in/yaml.v3" kyaml "sigs.k8s.io/yaml" - - "github.com/aquasecurity/trivy/pkg/log" ) -type Parser struct { - logger *log.Logger -} - -// New creates a new K8s parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("k8s parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string][]any, error) { - files := make(map[string][]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - parsed, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - - files[path] = parsed - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses Kubernetes manifest from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) ([]any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - return p.Parse(f, path) -} - -func (p *Parser) Parse(r io.Reader, path string) ([]any, error) { - +func Parse(_ context.Context, r io.Reader, path string) ([]any, error) { contents, err := io.ReadAll(r) if err != nil { return nil, err diff --git a/pkg/iac/scanners/kubernetes/scanner.go b/pkg/iac/scanners/kubernetes/scanner.go index dfd248cc8b1d..f79d59aaaf12 100644 --- a/pkg/iac/scanners/kubernetes/scanner.go +++ b/pkg/iac/scanners/kubernetes/scanner.go @@ -3,119 +3,17 @@ package kubernetes import ( "context" "io" - "io/fs" - "path/filepath" - "sort" - "sync" - "github.com/liamg/memoryfs" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser" "github.com/aquasecurity/trivy/pkg/iac/scanners/options" "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" ) -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - options []options.ScannerOption - regoScanner *rego.Scanner - parser *parser.Parser -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("k8s scanner"), - parser: parser.New(), - } - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) Name() string { - return "Kubernetes" -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceKubernetes, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil +func NewScanner(opts ...options.ScannerOption) *generic.GenericScanner { + return generic.NewScanner("Kubernetes", types.SourceKubernetes, generic.ParseFunc(parse), opts...) } -func (s *Scanner) ScanReader(ctx context.Context, filename string, reader io.Reader) (scan.Results, error) { - memfs := memoryfs.New() - if err := memfs.MkdirAll(filepath.Base(filename), 0o700); err != nil { - return nil, err - } - data, err := io.ReadAll(reader) - if err != nil { - return nil, err - } - if err := memfs.WriteFile(filename, data, 0o644); err != nil { - return nil, err - } - return s.ScanFS(ctx, memfs, ".") -} - -func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, dir string) (scan.Results, error) { - - k8sFilesets, err := s.parser.ParseFS(ctx, target, dir) - if err != nil { - return nil, err - } - - if len(k8sFilesets) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, k8sFiles := range k8sFilesets { - for _, content := range k8sFiles { - inputs = append(inputs, rego.Input{ - Path: path, - FS: target, - Contents: content, - }) - } - } - - regoScanner, err := s.initRegoScanner(target) - if err != nil { - return nil, err - } - - s.logger.Debug("Scanning files", log.Int("count", len(inputs))) - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", target, false) - - sort.Slice(results, func(i, j int) bool { - return results[i].Rule().AVDID < results[j].Rule().AVDID - }) - return results, nil +func parse(ctx context.Context, r io.Reader, path string) (any, error) { + return parser.Parse(ctx, r, path) } diff --git a/pkg/iac/scanners/kubernetes/scanner_test.go b/pkg/iac/scanners/kubernetes/scanner_test.go index ef1ac38560f4..c9186a7f9c40 100644 --- a/pkg/iac/scanners/kubernetes/scanner_test.go +++ b/pkg/iac/scanners/kubernetes/scanner_test.go @@ -1,24 +1,23 @@ -package kubernetes +package kubernetes_test import ( "context" - "os" + "io/fs" "strings" "testing" + "testing/fstest" + "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" "github.com/aquasecurity/trivy/pkg/iac/rego" "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes" ) -func Test_BasicScan_YAML(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/example.yaml": ` +func Test_ScanYAML(t *testing.T) { + file := ` apiVersion: v1 kind: Pod metadata: @@ -28,148 +27,48 @@ spec: - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] image: busybox name: hello -`, - "/rules/rule.rego": ` +` + fsys := buildFS(map[string]string{ + "code/example.yaml": file, + "checks/rule.rego": `# METADATA +# title: test check +# custom: +# id: KSV011 +# avd_id: AVD-KSV-0011 +# severity: LOW +# input: +# selector: +# - type: kubernetes package builtin.kubernetes.KSV011 import data.lib.kubernetes -import data.lib.utils - -default failLimitsCPU = false - -__rego_metadata__ := { - "id": "KSV011", - "avd_id": "AVD-KSV-0011", - "title": "CPU not limited", - "short_code": "limit-cpu", - "version": "v1.0.0", - "severity": "LOW", - "type": "Kubernetes Security Check", - "description": "Enforcing CPU limits prevents DoS via resource exhaustion.", - "recommended_actions": "Set a limit value under 'containers[].resources.limits.cpu'.", - "url": "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "kubernetes"}], -} - -# getLimitsCPUContainers returns all containers which have set resources.limits.cpu -getLimitsCPUContainers[container] { - allContainers := kubernetes.containers[_] - utils.has_key(allContainers.resources.limits, "cpu") - container := allContainers.name -} - -# getNoLimitsCPUContainers returns all containers which have not set -# resources.limits.cpu -getNoLimitsCPUContainers[container] { - container := kubernetes.containers[_].name - not getLimitsCPUContainers[container] -} - -# failLimitsCPU is true if containers[].resources.limits.cpu is not set -# for ANY container -failLimitsCPU { - count(getNoLimitsCPUContainers) > 0 -} deny[res] { - failLimitsCPU - - msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'resources.limits.cpu'", [getNoLimitsCPUContainers[_], kubernetes.kind, kubernetes.name])) - - res := { - "msg": msg, - "id": __rego_metadata__.id, - "title": __rego_metadata__.title, - "severity": __rego_metadata__.severity, - "type": __rego_metadata__.type, - "startline": 6, - "endline": 10, - } + container := kubernetes.containers[_] + res := result.new("fail", container) } `, }) - scanner := NewScanner( - rego.WithPolicyDirs("rules"), + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("checks"), rego.WithEmbeddedLibraries(true), ) - results, err := scanner.ScanFS(context.TODO(), fs, "code") + results, err := scanner.ScanFS(context.TODO(), fsys, "code") require.NoError(t, err) - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-KSV-0011", - Aliases: []string{"KSV011"}, - ShortCode: "limit-cpu", - Summary: "CPU not limited", - Explanation: "Enforcing CPU limits prevents DoS via resource exhaustion.", - Impact: "", - Resolution: "Set a limit value under 'containers[].resources.limits.cpu'.", - Provider: "kubernetes", - Service: "general", - Links: []string{"https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits"}, - Severity: "LOW", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.kubernetes.KSV011", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, results.GetFailed()[0].Rule()) + failed := results.GetFailed() + require.Len(t, failed, 1) - failure := results.GetFailed()[0] - actualCode, err := failure.GetCode() - require.NoError(t, err) - for i := range actualCode.Lines { - actualCode.Lines[i].Highlighted = "" - } - assert.Equal(t, []scan.Line{ - { - Number: 6, - Content: "spec: ", - IsCause: true, - FirstCause: true, - Annotation: "", - }, - { - Number: 7, - Content: " containers: ", - IsCause: true, - Annotation: "", - }, - { - Number: 8, - Content: " - command: [\"sh\", \"-c\", \"echo 'Hello' && sleep 1h\"]", - IsCause: true, - Annotation: "", - }, - { - Number: 9, - Content: " image: busybox", - IsCause: true, - Annotation: "", - }, - { - Number: 10, - Content: " name: hello", - IsCause: true, - LastCause: true, - Annotation: "", - }, - }, actualCode.Lines) + assert.Equal(t, "AVD-KSV-0011", failed[0].Rule().AVDID) + assertLines(t, file, failed) } -func Test_BasicScan_JSON(t *testing.T) { +func Test_ScanJSON(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "/code/example.json": ` + file := ` { "apiVersion": "v1", "kind": "Pod", @@ -190,165 +89,58 @@ func Test_BasicScan_JSON(t *testing.T) { ] } } -`, - "/rules/rule.rego": ` +` + + fsys := buildFS(map[string]string{ + "code/example.json": file, + "checks/rule.rego": `# METADATA +# title: test check +# custom: +# id: KSV011 +# avd_id: AVD-KSV-0011 +# severity: LOW +# input: +# selector: +# - type: kubernetes package builtin.kubernetes.KSV011 import data.lib.kubernetes -import data.lib.utils - -default failLimitsCPU = false - -__rego_metadata__ := { - "id": "KSV011", - "avd_id": "AVD-KSV-0011", - "title": "CPU not limited", - "short_code": "limit-cpu", - "version": "v1.0.0", - "severity": "LOW", - "type": "Kubernetes Security Check", - "description": "Enforcing CPU limits prevents DoS via resource exhaustion.", - "recommended_actions": "Set a limit value under 'containers[].resources.limits.cpu'.", - "url": "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "kubernetes"}], -} - -# getLimitsCPUContainers returns all containers which have set resources.limits.cpu -getLimitsCPUContainers[container] { - allContainers := kubernetes.containers[_] - utils.has_key(allContainers.resources.limits, "cpu") - container := allContainers.name -} - -# getNoLimitsCPUContainers returns all containers which have not set -# resources.limits.cpu -getNoLimitsCPUContainers[container] { - container := kubernetes.containers[_].name - not getLimitsCPUContainers[container] -} - -# failLimitsCPU is true if containers[].resources.limits.cpu is not set -# for ANY container -failLimitsCPU { - count(getNoLimitsCPUContainers) > 0 -} deny[res] { - failLimitsCPU - - msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'resources.limits.cpu'", [getNoLimitsCPUContainers[_], kubernetes.kind, kubernetes.name])) - - res := { - "msg": msg, - "id": __rego_metadata__.id, - "title": __rego_metadata__.title, - "severity": __rego_metadata__.severity, - "type": __rego_metadata__.type, - "startline": 6, - "endline": 10, - } + container := kubernetes.containers[_] + res := result.new("fail", container) } `, }) - scanner := NewScanner( - rego.WithPolicyDirs("rules"), + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("checks"), rego.WithEmbeddedLibraries(true), ) - results, err := scanner.ScanFS(context.TODO(), fs, "code") + results, err := scanner.ScanFS(context.TODO(), fsys, "code") require.NoError(t, err) require.Len(t, results.GetFailed(), 1) - assert.Equal(t, scan.Rule{ - AVDID: "AVD-KSV-0011", - Aliases: []string{"KSV011"}, - ShortCode: "limit-cpu", - Summary: "CPU not limited", - Explanation: "Enforcing CPU limits prevents DoS via resource exhaustion.", - Impact: "", - Resolution: "Set a limit value under 'containers[].resources.limits.cpu'.", - Provider: "kubernetes", - Service: "general", - Links: []string{"https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits"}, - Severity: "LOW", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.kubernetes.KSV011", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, results.GetFailed()[0].Rule()) + failed := results.GetFailed() + require.Len(t, failed, 1) - failure := results.GetFailed()[0] - actualCode, err := failure.GetCode() - require.NoError(t, err) - for i := range actualCode.Lines { - actualCode.Lines[i].Highlighted = "" - } - assert.Equal(t, []scan.Line{ - { - Number: 6, - Content: ` "name": "hello-cpu-limit"`, - IsCause: true, - FirstCause: true, - Annotation: "", - }, - { - Number: 7, - Content: ` },`, - IsCause: true, - Annotation: "", - }, - { - Number: 8, - Content: ` "spec": {`, - IsCause: true, - Annotation: "", - }, - { - Number: 9, - Content: ` "containers": [`, - IsCause: true, - Annotation: "", - }, - { - Number: 10, - Content: ` {`, - IsCause: true, - LastCause: true, - Annotation: "", - }, - }, actualCode.Lines) + assert.Equal(t, "AVD-KSV-0011", failed[0].Rule().AVDID) + assertLines(t, file, failed) } -func Test_FileScan(t *testing.T) { +func Test_YamlWithSeparator(t *testing.T) { - results, err := NewScanner(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true), rego.WithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -`)) - require.NoError(t, err) - - assert.NotEmpty(t, results.GetFailed()) -} - -func Test_FileScan_WithSeparator(t *testing.T) { + fsys := buildFS(map[string]string{ + "check.rego": `package defsec - results, err := NewScanner(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(` +deny[res] { + input.kind == "Pod" + res := result.new("fail", input) +}`, + "k8s.yaml": ` --- --- apiVersion: v1 @@ -360,13 +152,21 @@ spec: - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] image: busybox name: hello -`)) +`, + }) + + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("."), + rego.WithEmbeddedLibraries(true), + ) + results, err := scanner.ScanFS(context.TODO(), fsys, ".") require.NoError(t, err) assert.NotEmpty(t, results.GetFailed()) } -func Test_FileScan_MultiManifests(t *testing.T) { +func Test_YamlMultiDocument(t *testing.T) { file := ` --- apiVersion: v1 @@ -389,306 +189,71 @@ spec: image: busybox name: hello2 ` - - results, err := NewScanner( - rego.WithEmbeddedPolicies(true), - rego.WithEmbeddedLibraries(true), - rego.WithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(file)) - require.NoError(t, err) - - assert.Greater(t, len(results.GetFailed()), 1) - fileLines := strings.Split(file, "\n") - for _, failure := range results.GetFailed() { - actualCode, err := failure.GetCode() - require.NoError(t, err) - assert.NotEmpty(t, actualCode.Lines) - for _, line := range actualCode.Lines { - assert.Greater(t, len(fileLines), line.Number) - assert.Equal(t, line.Content, fileLines[line.Number-1]) - } - } -} - -func Test_FileScanWithPolicyReader(t *testing.T) { - - results, err := NewScanner(rego.WithPolicyReader(strings.NewReader(`package defsec - -deny[msg] { - msg = "fail" -} -`))).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -`)) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) -} - -func Test_FileScanJSON(t *testing.T) { - - results, err := NewScanner(rego.WithPolicyReader(strings.NewReader(`package defsec - -deny[msg] { - input.kind == "Pod" - msg = "fail" -} -`))).ScanReader(context.TODO(), "k8s.json", strings.NewReader(` -{ - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "mongo", - "labels": { - "name": "mongo", - "role": "mongo" - } - }, - "spec": { - "volumes": [ - { - "name": "mongo-disk", - "gcePersistentDisk": { - "pdName": "mongo-disk", - "fsType": "ext4" - } - } - ], - "containers": [ - { - "name": "mongo", - "image": "mongo:latest", - "ports": [ - { - "name": "mongo", - "containerPort": 27017 - } - ], - "volumeMounts": [ - { - "name": "mongo-disk", - "mountPath": "/data/db" - } - ] - } - ] - } -} -`)) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) -} - -func Test_FileScanWithMetadata(t *testing.T) { - - results, err := NewScanner( - rego.WithTrace(os.Stdout), - rego.WithPolicyReader(strings.NewReader(`package defsec - -deny[msg] { - input.kind == "Pod" - msg := { - "msg": "fail", - "startline": 2, - "endline": 2, - "filepath": "chartname/template/serviceAccount.yaml" - } -} -`))).ScanReader( - context.TODO(), - "k8s.yaml", - strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -`)) - require.NoError(t, err) - - assert.NotEmpty(t, results.GetFailed()) - - firstResult := results.GetFailed()[0] - assert.Equal(t, 2, firstResult.Metadata().Range().GetStartLine()) - assert.Equal(t, 2, firstResult.Metadata().Range().GetEndLine()) - assert.Equal(t, "chartname/template/serviceAccount.yaml", firstResult.Metadata().Range().GetFilename()) -} - -func Test_FileScanExampleWithResultFunction(t *testing.T) { - - results, err := NewScanner( - rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true), - rego.WithPolicyReader(strings.NewReader(`package defsec - -import data.lib.kubernetes - -default checkCapsDropAll = false - -__rego_metadata__ := { -"id": "KSV003", -"avd_id": "AVD-KSV-0003", -"title": "Default capabilities not dropped", -"short_code": "drop-default-capabilities", -"version": "v1.0.0", -"severity": "LOW", -"type": "Kubernetes Security Check", -"description": "The container should drop all default capabilities and add only those that are needed for its execution.", -"recommended_actions": "Add 'ALL' to containers[].securityContext.capabilities.drop.", -"url": "https://kubesec.io/basics/containers-securitycontext-capabilities-drop-index-all/", -} - -__rego_input__ := { -"combine": false, -"selector": [{"type": "kubernetes"}], -} - -# Get all containers which include 'ALL' in security.capabilities.drop -getCapsDropAllContainers[container] { -allContainers := kubernetes.containers[_] -lower(allContainers.securityContext.capabilities.drop[_]) == "all" -container := allContainers.name -} - -# Get all containers which don't include 'ALL' in security.capabilities.drop -getCapsNoDropAllContainers[container] { -container := kubernetes.containers[_] -not getCapsDropAllContainers[container.name] -} + fsys := buildFS(map[string]string{ + "check.rego": `package defsec deny[res] { -output := getCapsNoDropAllContainers[_] - -msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should add 'ALL' to 'securityContext.capabilities.drop'", [output.name, kubernetes.kind, kubernetes.name])) + input.kind == "Pod" + res := result.new("fail", input) +}`, + "k8s.yaml": file, + }) -res := result.new(msg, output) -} + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("."), + rego.WithEmbeddedLibraries(true), + ) -`))).ScanReader( - context.TODO(), - "k8s.yaml", - strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello - securityContext: - capabilities: - drop: - - nothing -`)) + results, err := scanner.ScanFS(context.TODO(), fsys, ".") require.NoError(t, err) - require.NotEmpty(t, results.GetFailed()) - - firstResult := results.GetFailed()[0] - assert.Equal(t, 8, firstResult.Metadata().Range().GetStartLine()) - assert.Equal(t, 14, firstResult.Metadata().Range().GetEndLine()) - assert.Equal(t, "k8s.yaml", firstResult.Metadata().Range().GetFilename()) + assertLines(t, file, results) } -func Test_checkPolicyIsApplicable(t *testing.T) { - srcFS := testutil.CreateFS(t, map[string]string{ - "policies/pod_policy.rego": `# METADATA -# title: "Process can elevate its own privileges" -# description: "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node." +func Test_CheckWithSubtype(t *testing.T) { + fsys := buildFS(map[string]string{ + "checks/pod_policy.rego": `# METADATA +# title: test check # scope: package # schemas: # - input: schema["kubernetes"] -# related_resources: -# - https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted # custom: # id: KSV001 -# avd_id: AVD-KSV-0999 +# avd_id: AVD-KSV-0001 # severity: MEDIUM -# short_code: no-self-privesc -# recommended_action: "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'." # input: # selector: # - type: kubernetes # subtypes: # - kind: Pod -package builtin.kubernetes.KSV999 +package builtin.kubernetes.KSV001 import data.lib.kubernetes -import data.lib.utils - -default checkAllowPrivilegeEscalation = false - -# getNoPrivilegeEscalationContainers returns the names of all containers which have -# securityContext.allowPrivilegeEscalation set to false. -getNoPrivilegeEscalationContainers[container] { - allContainers := kubernetes.containers[_] - allContainers.securityContext.allowPrivilegeEscalation == false - container := allContainers.name -} - -# getPrivilegeEscalationContainers returns the names of all containers which have -# securityContext.allowPrivilegeEscalation set to true or not set. -getPrivilegeEscalationContainers[container] { - containerName := kubernetes.containers[_].name - not getNoPrivilegeEscalationContainers[containerName] - container := kubernetes.containers[_] -} deny[res] { - output := getPrivilegeEscalationContainers[_] - msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'securityContext.allowPrivilegeEscalation' to false", [output.name, kubernetes.kind, kubernetes.name])) - res := result.new(msg, output) + res := result.new("fail", input) } - `, - "policies/namespace_policy.rego": `# METADATA -# title: "The default namespace should not be used" -# description: "ensure that default namespace should not be used" + "checks/namespace_policy.rego": `# METADATA +# title: test check 2 # scope: package # schemas: # - input: schema["kubernetes"] -# related_resources: -# - https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ # custom: -# id: KSV110 -# avd_id: AVD-KSV-0888 +# id: KSV002 +# avd_id: AVD-KSV-0002 # severity: LOW -# short_code: default-namespace-should-not-be-used -# recommended_action: "Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace." # input: # selector: # - type: kubernetes # subtypes: # - kind: Namespace -package builtin.kubernetes.KSV888 - -import data.lib.kubernetes - -default defaultNamespaceInUse = false - -defaultNamespaceInUse { - kubernetes.namespace == "default" -} +package builtin.kubernetes.KSV002 deny[res] { - defaultNamespaceInUse - msg := sprintf("%s '%s' should not be set with 'default' namespace", [kubernetes.kind, kubernetes.name]) - res := result.new(msg, input.metadata.namespace) + res := result.new("fail", input) } - `, "test/KSV001/pod.yaml": `apiVersion: v1 kind: Pod @@ -706,18 +271,37 @@ spec: `, }) - scanner := NewScanner( - // rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true), + scanner := kubernetes.NewScanner( rego.WithEmbeddedLibraries(true), - rego.WithPolicyDirs("policies/"), - rego.WithPolicyFilesystem(srcFS), + rego.WithPolicyDirs("checks"), + rego.WithPolicyFilesystem(fsys), ) - results, err := scanner.ScanFS(context.TODO(), srcFS, "test/KSV001") + results, err := scanner.ScanFS(context.TODO(), fsys, "test/KSV001") require.NoError(t, err) require.NoError(t, err) require.Len(t, results.GetFailed(), 1) - failure := results.GetFailed()[0].Rule() - assert.Equal(t, "Process can elevate its own privileges", failure.Summary) + failure := results.GetFailed()[0] + + assert.Equal(t, "AVD-KSV-0001", failure.Rule().AVDID) +} + +func assertLines(t *testing.T, content string, results scan.Results) { + lines := strings.Split(content, "\n") + for _, res := range results { + actualCode, err := res.GetCode() + require.NoError(t, err) + assert.NotEmpty(t, actualCode.Lines) + for _, line := range actualCode.Lines { + assert.Greater(t, len(lines), line.Number) + assert.Equal(t, line.Content, lines[line.Number-1]) + } + } +} + +func buildFS(files map[string]string) fs.FS { + return fstest.MapFS(lo.MapValues(files, func(val string, _ string) *fstest.MapFile { + return &fstest.MapFile{Data: []byte(val)} + })) } diff --git a/pkg/iac/scanners/terraform/executor/executor.go b/pkg/iac/scanners/terraform/executor/executor.go index 452e47dcc8de..8e14f778e5b4 100644 --- a/pkg/iac/scanners/terraform/executor/executor.go +++ b/pkg/iac/scanners/terraform/executor/executor.go @@ -85,6 +85,7 @@ func (e *Executor) Execute(modules terraform.Modules) (scan.Results, error) { "ignore": attributeIgnorer(modules), } + // ignore a result based on user input results.Ignore(ignores, ignorers) for _, ignored := range results.GetIgnored() { diff --git a/pkg/iac/scanners/terraform/module_test.go b/pkg/iac/scanners/terraform/module_test.go index d0d289a9d562..ec4291fd59e4 100644 --- a/pkg/iac/scanners/terraform/module_test.go +++ b/pkg/iac/scanners/terraform/module_test.go @@ -567,7 +567,7 @@ resource "something" "else" { for_each = toset(["true"]) content { - ok = each.value + ok = blah.value } } } diff --git a/pkg/iac/scanners/terraform/parser/evaluator.go b/pkg/iac/scanners/terraform/parser/evaluator.go index 809498f35c63..8e2e737b5d9b 100644 --- a/pkg/iac/scanners/terraform/parser/evaluator.go +++ b/pkg/iac/scanners/terraform/parser/evaluator.go @@ -260,36 +260,17 @@ func (e *evaluator) evaluateSteps() { } func (e *evaluator) expandBlocks(blocks terraform.Blocks) terraform.Blocks { - return e.expandDynamicBlocks(e.expandBlockForEaches(e.expandBlockCounts(blocks), false)...) + return e.expandDynamicBlocks(e.expandBlockForEaches(e.expandBlockCounts(blocks))...) } func (e *evaluator) expandDynamicBlocks(blocks ...*terraform.Block) terraform.Blocks { for _, b := range blocks { - e.expandDynamicBlock(b) - } - return blocks -} - -func (e *evaluator) expandDynamicBlock(b *terraform.Block) { - for _, sub := range b.AllBlocks() { - e.expandDynamicBlock(sub) - } - for _, sub := range b.AllBlocks().OfType("dynamic") { - if sub.IsExpanded() { - continue - } - blockName := sub.TypeLabel() - expanded := e.expandBlockForEaches(terraform.Blocks{sub}, true) - for _, ex := range expanded { - if content := ex.GetBlock("content"); content.IsNotNil() { - _ = e.expandDynamicBlocks(content) - b.InjectBlock(content, blockName) - } - } - if len(expanded) > 0 { - sub.MarkExpanded() + if err := b.ExpandBlock(); err != nil { + e.logger.Error(`Failed to expand dynamic block.`, + log.String("block", b.FullName()), log.Err(err)) } } + return blocks } func isBlockSupportsForEachMetaArgument(block *terraform.Block) bool { @@ -297,11 +278,10 @@ func isBlockSupportsForEachMetaArgument(block *terraform.Block) bool { "module", "resource", "data", - "dynamic", }, block.Type()) } -func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool) terraform.Blocks { +func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks) terraform.Blocks { var forEachFiltered terraform.Blocks @@ -348,7 +328,7 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool // is the value of the collection. The exception is the use of for-each inside a dynamic block, // because in this case the collection element may not be a primitive value. if (forEachVal.Type().IsCollectionType() || forEachVal.Type().IsTupleType()) && - !forEachVal.Type().IsMapType() && !isDynamic { + !forEachVal.Type().IsMapType() { stringVal, err := convert.Convert(val, cty.String) if err != nil { e.logger.Error( @@ -374,22 +354,7 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool ctx.Set(eachObj, "each") ctx.Set(eachObj, block.TypeLabel()) - - if isDynamic { - if iterAttr := block.GetAttribute("iterator"); iterAttr.IsNotNil() { - refs := iterAttr.AllReferences() - if len(refs) == 1 { - ctx.Set(idx, refs[0].TypeLabel(), "key") - ctx.Set(val, refs[0].TypeLabel(), "value") - } else { - e.logger.Debug("Ignoring iterator attribute in dynamic block, expected one reference", - log.Int("refs", len(refs))) - } - } - } - forEachFiltered = append(forEachFiltered, clone) - clones[idx.AsString()] = clone.Values() }) diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index e3bd817748f6..540338afb57f 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -1367,139 +1367,284 @@ func TestCountMetaArgumentInModule(t *testing.T) { } func TestDynamicBlocks(t *testing.T) { - t.Run("arg is list of int", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "aws_security_group" "sg-webserver" { - vpc_id = "1111" - dynamic "ingress" { + tests := []struct { + name string + src string + expected []any + }{ + { + name: "for-each use tuple of int", + src: `resource "test_resource" "test" { + dynamic "foo" { for_each = [80, 443] content { - from_port = ingress.value - to_port = ingress.value - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + bar = foo.value } } -} -`, - }) - require.Len(t, modules, 1) - - secGroups := modules.GetResourcesByType("aws_security_group") - assert.Len(t, secGroups, 1) - ingressBlocks := secGroups[0].GetBlocks("ingress") - assert.Len(t, ingressBlocks, 2) - - var inboundPorts []int - for _, ingress := range ingressBlocks { - fromPort := ingress.GetAttribute("from_port").AsIntValueOrDefault(-1, ingress).Value() - inboundPorts = append(inboundPorts, fromPort) - } - - assert.True(t, compareSets([]int{80, 443}, inboundPorts)) - }) - - t.Run("empty for-each", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "aws_lambda_function" "analyzer" { - dynamic "vpc_config" { +}`, + expected: []any{float64(80), float64(443)}, + }, + { + name: "for-each use list of int", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = tolist([80, 443]) + content { + bar = foo.value + } + } +}`, + expected: []any{float64(80), float64(443)}, + }, + { + name: "for-each use set of int", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = toset([80, 443]) + content { + bar = foo.value + } + } +}`, + expected: []any{float64(80), float64(443)}, + }, + { + name: "for-each use list of bool", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = tolist([true]) + content { + bar = foo.value + } + } +}`, + expected: []any{true}, + }, + { + name: "empty for-each", + src: `resource "test_resource" "test" { + dynamic "foo" { for_each = [] content {} } +}`, + expected: []any{}, + }, + { + name: "for-each use tuple of objects", + src: `variable "test_var" { + type = list(object({ enabled = bool })) + default = [{ enabled = true }] } -`, - }) - require.Len(t, modules, 1) - functions := modules.GetResourcesByType("aws_lambda_function") - assert.Len(t, functions, 1) - vpcConfigs := functions[0].GetBlocks("vpc_config") - assert.Empty(t, vpcConfigs) - }) +resource "test_resource" "test" { + dynamic "foo" { + for_each = var.test_var - t.Run("arg is list of bool", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "aws_lambda_function" "analyzer" { - dynamic "vpc_config" { - for_each = [true] - content {} + content { + bar = foo.value.enabled + } + } +}`, + expected: []any{true}, + }, + { + name: "attribute ref to object key", + src: `variable "some_var" { + type = map( + object({ + tag = string + }) + ) + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } } } -`, - }) - require.Len(t, modules, 1) - - functions := modules.GetResourcesByType("aws_lambda_function") - assert.Len(t, functions, 1) - vpcConfigs := functions[0].GetBlocks("vpc_config") - assert.Len(t, vpcConfigs, 1) - }) - t.Run("arg is list of objects", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": `locals { - cluster_network_policy = [{ - enabled = true - }] +resource "test_resource" "test" { + dynamic "foo" { + for_each = { for name, values in var.some_var : name => values } + content { + bar = foo.key + } + } +}`, + expected: []any{"ssh", "http", "https"}, + }, + { + name: "attribute ref to object value", + src: `variable "some_var" { + type = map( + object({ + tag = string + }) + ) + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } + } } -resource "google_container_cluster" "primary" { - name = "test" +resource "test_resource" "test" { + dynamic "foo" { + for_each = { for name, values in var.some_var : name => values } + content { + bar = foo.value.tag + } + } +}`, + expected: []any{"login", "proxy", "proxy"}, + }, + { + name: "attribute ref to map key", + src: `variable "some_var" { + type = map + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } + } +} - dynamic "network_policy" { - for_each = local.cluster_network_policy +resource "test_resource" "test" { + dynamic "foo" { + for_each = var.some_var + content { + bar = foo.key + } + } +}`, + expected: []any{"ssh", "http", "https"}, + }, + { + name: "attribute ref to map value", + src: `variable "some_var" { + type = map + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } + } +} +resource "test_resource" "test" { + dynamic "foo" { + for_each = var.some_var content { - enabled = network_policy.value.enabled + bar = foo.value.tag } } }`, - }) - require.Len(t, modules, 1) + expected: []any{"login", "proxy", "proxy"}, + }, + { + name: "dynamic block with iterator", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = ["foo", "bar"] + iterator = some_iterator + content { + bar = some_iterator.value + } + } +}`, + expected: []any{"foo", "bar"}, + }, + { + name: "iterator and parent block with same name", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = ["foo", "bar"] + iterator = foo + content { + bar = foo.value + } + } +}`, + expected: []any{"foo", "bar"}, + }, + { + name: "for-each use null value", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = null + content { + bar = foo.value + } + } +}`, + expected: []any{}, + }, + { + name: "no for-each attribute", + src: `resource "test_resource" "test" { + dynamic "foo" { + content { + bar = foo.value + } + } +}`, + expected: []any{}, + }, + } - clusters := modules.GetResourcesByType("google_container_cluster") - assert.Len(t, clusters, 1) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + modules := parse(t, map[string]string{ + "main.tf": tt.src, + }) + require.Len(t, modules, 1) - networkPolicies := clusters[0].GetBlocks("network_policy") - assert.Len(t, networkPolicies, 1) + resource := modules.GetResourcesByType("test_resource") + require.Len(t, resource, 1) + blocks := resource[0].GetBlocks("foo") - enabled := networkPolicies[0].GetAttribute("enabled") - assert.True(t, enabled.Value().True()) - }) + var vals []any + for _, attr := range blocks { + vals = append(vals, attr.GetAttribute("bar").GetRawValue()) + } - t.Run("nested dynamic", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "test_block" "this" { - name = "name" - location = "loc" - dynamic "env" { - for_each = ["1", "2"] - content { - dynamic "value_source" { - for_each = [true, true] - content {} - } + assert.ElementsMatch(t, tt.expected, vals) + }) } +} + +func TestNestedDynamicBlock(t *testing.T) { + modules := parse(t, map[string]string{ + "main.tf": `resource "test_resource" "test" { + dynamic "foo" { + for_each = ["1", "1"] + content { + dynamic "bar" { + for_each = [true, true] + content { + baz = foo.value + qux = bar.value + } + } + } } }`, - }) - require.Len(t, modules, 1) + }) + require.Len(t, modules, 1) - testResources := modules.GetResourcesByType("test_block") - assert.Len(t, testResources, 1) - envs := testResources[0].GetBlocks("env") - assert.Len(t, envs, 2) + testResources := modules.GetResourcesByType("test_resource") + assert.Len(t, testResources, 1) + blocks := testResources[0].GetBlocks("foo") + assert.Len(t, blocks, 2) - var sources []*terraform.Block - for _, env := range envs { - sources = append(sources, env.GetBlocks("value_source")...) + var nested []*terraform.Block + for _, block := range blocks { + nested = append(nested, block.GetBlocks("bar")...) + for _, b := range nested { + assert.Equal(t, "1", b.GetAttribute("baz").GetRawValue()) + assert.Equal(t, true, b.GetAttribute("qux").GetRawValue()) } - assert.Len(t, sources, 4) - }) + } + assert.Len(t, nested, 4) } func parse(t *testing.T, files map[string]string) terraform.Modules { @@ -1513,21 +1658,6 @@ func parse(t *testing.T, files map[string]string) terraform.Modules { return modules } -func compareSets(a, b []int) bool { - m := make(map[int]bool) - for _, el := range a { - m[el] = true - } - - for _, el := range b { - if !m[el] { - return false - } - } - - return true -} - func TestModuleRefersToOutputOfAnotherModule(t *testing.T) { files := map[string]string{ "main.tf": ` @@ -1775,42 +1905,6 @@ variable "foo" {} assert.Equal(t, "bar", blocks[0].GetAttribute("foo").Value().AsString()) } -func TestDynamicWithIterator(t *testing.T) { - fsys := fstest.MapFS{ - "main.tf": &fstest.MapFile{ - Data: []byte(`resource "aws_s3_bucket" "this" { - dynamic versioning { - for_each = [true] - iterator = ver - - content { - enabled = ver.value - } - } -}`), - }, - } - - parser := New( - fsys, "", - OptionStopOnHCLError(true), - OptionWithDownloads(false), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - assert.Len(t, modules, 1) - - buckets := modules.GetResourcesByType("aws_s3_bucket") - assert.Len(t, buckets, 1) - - attr, _ := buckets[0].GetNestedAttribute("versioning.enabled") - - assert.True(t, attr.Value().True()) -} - func Test_AWSRegionNameDefined(t *testing.T) { fs := testutil.CreateFS(t, map[string]string{ diff --git a/pkg/iac/scanners/toml/parser/parser.go b/pkg/iac/scanners/toml/parser/parser.go deleted file mode 100644 index 95444389d74d..000000000000 --- a/pkg/iac/scanners/toml/parser/parser.go +++ /dev/null @@ -1,65 +0,0 @@ -package parser - -import ( - "context" - "io/fs" - "path/filepath" - - "github.com/BurntSushi/toml" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger -} - -// New creates a new parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("toml parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string]any, error) { - - files := make(map[string]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses toml content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) (any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - var target any - if _, err := toml.NewDecoder(f).Decode(&target); err != nil { - return nil, err - } - return target, nil -} diff --git a/pkg/iac/scanners/toml/parser/parser_test.go b/pkg/iac/scanners/toml/parser/parser_test.go deleted file mode 100644 index 1c9e9bdc341e..000000000000 --- a/pkg/iac/scanners/toml/parser/parser_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package parser - -import ( - "context" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Parser(t *testing.T) { - input := ` -[x] -y = 123 -z = ["a", "b", "c"] -` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.yaml", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.yaml") - require.NoError(t, err) - - msi, ok := data.(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int64) - require.True(t, ok) - - assert.Equal(t, int64(123), y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - -} diff --git a/pkg/iac/scanners/toml/scanner.go b/pkg/iac/scanners/toml/scanner.go deleted file mode 100644 index 216d0b1f9697..000000000000 --- a/pkg/iac/scanners/toml/scanner.go +++ /dev/null @@ -1,111 +0,0 @@ -package toml - -import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/toml/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - options []options.ScannerOption - parser *parser.Parser - regoScanner *rego.Scanner -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func (s *Scanner) Name() string { - return "TOML" -} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("toml scanner"), - } - for _, opt := range opts { - opt(s) - } - s.parser = parser.New() - return s -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - files, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(files) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, file := range files { - inputs = append(inputs, rego.Input{ - Path: path, - Contents: file, - FS: fsys, - }) - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - parsed, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.FilePath(path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: parsed, - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceTOML, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil -} diff --git a/pkg/iac/scanners/toml/scanner_test.go b/pkg/iac/scanners/toml/scanner_test.go deleted file mode 100644 index ea5819448288..000000000000 --- a/pkg/iac/scanners/toml/scanner_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package toml - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_BasicScan(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/code.toml": ` -[x] -y = 123 -z = ["a", "b", "c"] -`, - "/rules/rule.rego": `package builtin.toml.lol - -__rego_metadata__ := { - "id": "ABC123", - "avd_id": "AVD-AB-0123", - "title": "title", - "short_code": "short", - "severity": "CRITICAL", - "type": "TOML Check", - "description": "description", - "recommended_actions": "actions", - "url": "https://example.com", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "toml"}], -} - -deny[res] { - input.x.y == 123 - res := { - "msg": "oh no", - "startline": 1, - "endline": 2, - } -} - -`, - }) - - scanner := NewScanner(rego.WithPolicyDirs("rules")) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-AB-0123", - Aliases: []string{"ABC123"}, - ShortCode: "short", - Summary: "title", - Explanation: "description", - Impact: "", - Resolution: "actions", - Provider: "toml", - Service: "general", - Links: []string{"https://example.com"}, - Severity: "CRITICAL", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{ - Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.toml.lol", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, - results.GetFailed()[0].Rule(), - ) -} diff --git a/pkg/iac/scanners/yaml/parser/parser.go b/pkg/iac/scanners/yaml/parser/parser.go deleted file mode 100644 index 9b1f026ae248..000000000000 --- a/pkg/iac/scanners/yaml/parser/parser.go +++ /dev/null @@ -1,86 +0,0 @@ -package parser - -import ( - "bytes" - "context" - "io" - "io/fs" - "path/filepath" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger -} - -// New creates a new YAML parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("yaml parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string][]any, error) { - - files := make(map[string][]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses yaml content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) ([]any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - - contents, err := io.ReadAll(f) - if err != nil { - return nil, err - } - - var results []any - - marker := []byte("\n---\n") - altMarker := []byte("\r\n---\r\n") - if bytes.Contains(contents, altMarker) { - marker = altMarker - } - - for _, partial := range bytes.Split(contents, marker) { - var target any - if err := yaml.Unmarshal(partial, &target); err != nil { - return nil, err - } - results = append(results, target) - } - - return results, nil -} diff --git a/pkg/iac/scanners/yaml/parser/parser_test.go b/pkg/iac/scanners/yaml/parser/parser_test.go deleted file mode 100644 index f7817d5990fa..000000000000 --- a/pkg/iac/scanners/yaml/parser/parser_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package parser - -import ( - "context" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Parser(t *testing.T) { - input := `--- -x: - y: 123 - z: - - a - - b - - c -` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.yaml", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.yaml") - require.NoError(t, err) - - assert.Len(t, data, 1) - - msi, ok := data[0].(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int) - require.True(t, ok) - - assert.Equal(t, 123, y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - -} - -func Test_Parser_WithSeparatedContent(t *testing.T) { - input := `--- -x: - y: 123 - z: - - a - - b - - c ---- -x: - y: 456 - z: - - x - - y - - z -` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.yaml", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.yaml") - require.NoError(t, err) - - assert.Len(t, data, 2) - - { - msi, ok := data[0].(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int) - require.True(t, ok) - - assert.Equal(t, 123, y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - } - - { - msi, ok := data[1].(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int) - require.True(t, ok) - - assert.Equal(t, 456, y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "x", z[0]) - assert.Equal(t, "y", z[1]) - assert.Equal(t, "z", z[2]) - } - -} diff --git a/pkg/iac/scanners/yaml/scanner.go b/pkg/iac/scanners/yaml/scanner.go deleted file mode 100644 index 565d10df1f83..000000000000 --- a/pkg/iac/scanners/yaml/scanner.go +++ /dev/null @@ -1,113 +0,0 @@ -package yaml - -import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/yaml/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - options []options.ScannerOption - logger *log.Logger - parser *parser.Parser - regoScanner *rego.Scanner -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func (s *Scanner) Name() string { - return "YAML" -} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("yaml scanner"), - parser: parser.New(), - } - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - fileset, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(fileset) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, files := range fileset { - for _, file := range files { - inputs = append(inputs, rego.Input{ - Path: path, - Contents: file, - FS: fsys, - }) - } - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - parsed, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.String("path", path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: parsed, - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceYAML, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil -} diff --git a/pkg/iac/scanners/yaml/scanner_test.go b/pkg/iac/scanners/yaml/scanner_test.go deleted file mode 100644 index b7c4ec623887..000000000000 --- a/pkg/iac/scanners/yaml/scanner_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package yaml - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_BasicScan(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/data.yaml": `--- -x: - y: 123 - z: - - a - - b - - c -`, - "/rules/rule.rego": `package builtin.yaml.lol - -__rego_metadata__ := { - "id": "ABC123", - "avd_id": "AVD-AB-0123", - "title": "title", - "short_code": "short", - "severity": "CRITICAL", - "type": "YAML Check", - "description": "description", - "recommended_actions": "actions", - "url": "https://example.com", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "yaml"}], -} - -deny[res] { - input.x.y == 123 - res := { - "msg": "oh no", - "startline": 1, - "endline": 2, - } -} - -`, - }) - - scanner := NewScanner(rego.WithPolicyDirs("rules")) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-AB-0123", - Aliases: []string{"ABC123"}, - ShortCode: "short", - Summary: "title", - Explanation: "description", - Impact: "", - Resolution: "actions", - Provider: "yaml", - Service: "general", - Links: []string{"https://example.com"}, - Severity: "CRITICAL", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{ - Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.yaml.lol", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, - results.GetFailed()[0].Rule(), - ) -} diff --git a/pkg/iac/state/state_test.go b/pkg/iac/state/state_test.go index 60b4b061a2d3..46de9b506d4b 100644 --- a/pkg/iac/state/state_test.go +++ b/pkg/iac/state/state_test.go @@ -44,6 +44,7 @@ func Test_RegoConversion(t *testing.T) { "startline": 2, "endline": 4, "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", }, @@ -55,6 +56,7 @@ func Test_RegoConversion(t *testing.T) { "endline": 3, "value": "my-bucket", "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", }, diff --git a/pkg/iac/terraform/attribute.go b/pkg/iac/terraform/attribute.go index 39161a72a379..9c5536357283 100644 --- a/pkg/iac/terraform/attribute.go +++ b/pkg/iac/terraform/attribute.go @@ -729,15 +729,16 @@ func (a *Attribute) IsTrue() bool { if a == nil { return false } - switch a.Value().Type() { + val := a.Value() + switch val.Type() { case cty.Bool: - return a.Value().True() + return val.True() case cty.String: - val := a.Value().AsString() + val := val.AsString() val = strings.Trim(val, "\"") return strings.EqualFold(val, "true") case cty.Number: - val := a.Value().AsBigFloat() + val := val.AsBigFloat() f, _ := val.Float64() return f > 0 } diff --git a/pkg/iac/terraform/block.go b/pkg/iac/terraform/block.go index dbf7352959d0..348f938d4559 100644 --- a/pkg/iac/terraform/block.go +++ b/pkg/iac/terraform/block.go @@ -1,12 +1,14 @@ package terraform import ( + "errors" "fmt" "io/fs" "strconv" "strings" "github.com/google/uuid" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/zclconf/go-cty/cty" @@ -137,16 +139,15 @@ func (b *Block) GetRawValue() any { return nil } -func (b *Block) InjectBlock(block *Block, name string) { - block.hclBlock.Labels = []string{} - block.hclBlock.Type = name +func (b *Block) injectBlock(block *Block) { for attrName, attr := range block.Attributes() { - b.context.Root().SetByDot(attr.Value(), fmt.Sprintf("%s.%s.%s", b.reference.String(), name, attrName)) + path := fmt.Sprintf("%s.%s.%s", b.reference.String(), block.hclBlock.Type, attrName) + b.context.Root().SetByDot(attr.Value(), path) } b.childBlocks = append(b.childBlocks, block) } -func (b *Block) MarkExpanded() { +func (b *Block) markExpanded() { b.expanded = true } @@ -154,17 +155,26 @@ func (b *Block) IsExpanded() bool { return b.expanded } -func (b *Block) Clone(index cty.Value) *Block { - var childCtx *context.Context - if b.context != nil { - childCtx = b.context.NewChild() - } else { - childCtx = context.NewContext(&hcl.EvalContext{}, nil) +func (b *Block) inherit(ctx *context.Context, index ...cty.Value) *Block { + return NewBlock(b.copyBlock(), ctx, b.moduleBlock, b.parentBlock, b.moduleSource, b.moduleFS, index...) +} + +func (b *Block) copyBlock() *hcl.Block { + hclBlock := *b.hclBlock + return &hclBlock +} + +func (b *Block) childContext() *context.Context { + if b.context == nil { + return context.NewContext(&hcl.EvalContext{}, nil) } + return b.context.NewChild() +} - cloneHCL := *b.hclBlock +func (b *Block) Clone(index cty.Value) *Block { + childCtx := b.childContext() + clone := b.inherit(childCtx, index) - clone := NewBlock(&cloneHCL, childCtx, b.moduleBlock, b.parentBlock, b.moduleSource, b.moduleFS, index) if len(clone.hclBlock.Labels) > 0 { position := len(clone.hclBlock.Labels) - 1 labels := make([]string, len(clone.hclBlock.Labels)) @@ -188,7 +198,7 @@ func (b *Block) Clone(index cty.Value) *Block { } indexVal, _ := gocty.ToCtyValue(index, cty.Number) clone.context.SetByDot(indexVal, "count.index") - clone.MarkExpanded() + clone.markExpanded() b.cloneIndex++ return clone } @@ -446,6 +456,17 @@ func (b *Block) LocalName() string { return b.reference.String() } +func (b *Block) FullLocalName() string { + if b.parentBlock != nil { + return fmt.Sprintf( + "%s.%s", + b.parentBlock.FullLocalName(), + b.LocalName(), + ) + } + return b.LocalName() +} + func (b *Block) FullName() string { if b.moduleBlock != nil { @@ -576,3 +597,118 @@ func (b *Block) IsNil() bool { func (b *Block) IsNotNil() bool { return !b.IsNil() } + +func (b *Block) ExpandBlock() error { + var ( + expanded []*Block + errs error + ) + + for _, child := range b.childBlocks { + if child.Type() == "dynamic" { + blocks, err := child.expandDynamic() + if err != nil { + errs = multierror.Append(errs, err) + continue + } + expanded = append(expanded, blocks...) + } + } + + for _, block := range expanded { + b.injectBlock(block) + } + + return errs +} + +func (b *Block) expandDynamic() ([]*Block, error) { + if b.IsExpanded() || b.Type() != "dynamic" { + return nil, nil + } + + realBlockType := b.TypeLabel() + if realBlockType == "" { + return nil, errors.New("dynamic block must have 1 label") + } + + forEachVal, err := b.validateForEach() + if err != nil { + return nil, fmt.Errorf("invalid for-each in %s block: %w", b.FullLocalName(), err) + } + + var ( + expanded []*Block + errs error + ) + + forEachVal.ForEachElement(func(key, val cty.Value) (stop bool) { + if val.IsNull() { + return + } + + iteratorName, err := b.iteratorName(realBlockType) + if err != nil { + errs = multierror.Append(errs, err) + return + } + + forEachCtx := b.childContext() + obj := cty.ObjectVal(map[string]cty.Value{ + "key": key, + "value": val, + }) + forEachCtx.Set(obj, iteratorName) + + if content := b.GetBlock("content"); content != nil { + inherited := content.inherit(forEachCtx) + inherited.hclBlock.Labels = []string{} + inherited.hclBlock.Type = realBlockType + if err := inherited.ExpandBlock(); err != nil { + errs = multierror.Append(errs, err) + return + } + expanded = append(expanded, inherited) + } + return + }) + + if len(expanded) > 0 { + b.markExpanded() + } + + return expanded, errs +} + +func (b *Block) validateForEach() (cty.Value, error) { + forEachAttr := b.GetAttribute("for_each") + if forEachAttr == nil { + return cty.NilVal, errors.New("for_each attribute required") + } + + forEachVal := forEachAttr.Value() + + if !forEachVal.CanIterateElements() { + return cty.NilVal, fmt.Errorf("cannot use a %s value in for_each. An iterable collection is required", forEachVal.GoString()) + } + + return forEachVal, nil +} + +func (b *Block) iteratorName(blockType string) (string, error) { + iteratorAttr := b.GetAttribute("iterator") + if iteratorAttr == nil { + return blockType, nil + } + + traversal, diags := hcl.AbsTraversalForExpr(iteratorAttr.hclAttribute.Expr) + if diags.HasErrors() { + return "", diags + } + + if len(traversal) != 1 { + return "", fmt.Errorf("dynamic iterator must be a single variable name") + } + + return traversal.RootName(), nil +} diff --git a/pkg/iac/types/metadata.go b/pkg/iac/types/metadata.go index 359654bdc43b..c71460441622 100755 --- a/pkg/iac/types/metadata.go +++ b/pkg/iac/types/metadata.go @@ -84,6 +84,7 @@ func (m *Metadata) ToRego() any { "sourceprefix": m.Range().GetSourcePrefix(), "managed": m.isManaged, "explicit": m.isExplicit, + "unresolvable": m.isUnresolvable, "fskey": CreateFSKey(m.Range().GetFS()), "resource": m.Reference(), } diff --git a/pkg/iac/types/metadata_test.go b/pkg/iac/types/metadata_test.go index f447c6d85ca2..f7d46bbf9dbd 100644 --- a/pkg/iac/types/metadata_test.go +++ b/pkg/iac/types/metadata_test.go @@ -8,12 +8,14 @@ import ( func Test_MetadataToRego(t *testing.T) { m1 := NewTestMetadata() + m1.isUnresolvable = true expected := map[string]any{ "endline": 123, "explicit": false, "filepath": "test.test", "fskey": "", "managed": true, + "unresolvable": true, "resource": "", "sourceprefix": "", "startline": 123, @@ -27,6 +29,7 @@ func Test_MetadataToRego(t *testing.T) { "filepath": "test.test", "fskey": "", "managed": true, + "unresolvable": false, "resource": "", "sourceprefix": "", "startline": 123, diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index 6d5b589bb5d1..835730109b02 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -104,10 +104,10 @@ func (u *Updater) downloadDB(ctx context.Context) error { Quiet: u.quiet, } if err := artifacts.Download(ctx, u.dbDir, downloadOpt); err != nil { - return xerrors.Errorf("failed to download vulnerability DB: %w", err) + return xerrors.Errorf("failed to download Java DB: %w", err) } - return xerrors.New("failed to download Java DB from any source") + return nil } func Init(cacheDir string, javaDBRepositories []name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) { diff --git a/pkg/k8s/report/report.go b/pkg/k8s/report/report.go index e71c218bf864..947d39de14b8 100644 --- a/pkg/k8s/report/report.go +++ b/pkg/k8s/report/report.go @@ -57,9 +57,9 @@ type Resource struct { Namespace string `json:",omitempty"` Kind string Name string - Metadata types.Metadata `json:",omitempty"` - Results types.Results `json:",omitempty"` - Error string `json:",omitempty"` + Metadata []types.Metadata `json:",omitempty"` + Results types.Results `json:",omitempty"` + Error string `json:",omitempty"` // original report Report types.Report `json:"-"` @@ -90,8 +90,13 @@ func (r Report) consolidate() ConsolidatedReport { for _, m := range r.Resources { if vulnerabilitiesOrSecretResource(m) { vulnerabilities = append(vulnerabilities, m) - } else { + } + if misconfigsResource(m) { + res, ok := index[m.fullname()] index[m.fullname()] = m + if ok { + index[m.fullname()].Results[0].Misconfigurations = append(index[m.fullname()].Results[0].Misconfigurations, res.Results[0].Misconfigurations...) + } } } @@ -99,11 +104,15 @@ func (r Report) consolidate() ConsolidatedReport { key := v.fullname() if res, ok := index[key]; ok { + // Combine metadata + metadata := lo.UniqBy(append(res.Metadata, v.Metadata...), func(x types.Metadata) string { + return x.ImageID + }) index[key] = Resource{ Namespace: res.Namespace, Kind: res.Kind, Name: res.Name, - Metadata: res.Metadata, + Metadata: metadata, Results: append(res.Results, v.Results...), Error: res.Error, } @@ -214,7 +223,7 @@ func infraResource(misConfig Resource) bool { func CreateResource(artifact *artifacts.Artifact, report types.Report, err error) Resource { r := createK8sResource(artifact, report.Results) - r.Metadata = report.Metadata + r.Metadata = []types.Metadata{report.Metadata} r.Report = report // if there was any error during the scan if err != nil { @@ -244,7 +253,7 @@ func createK8sResource(artifact *artifacts.Artifact, scanResults types.Results) Namespace: artifact.Namespace, Kind: artifact.Kind, Name: artifact.Name, - Metadata: types.Metadata{}, + Metadata: []types.Metadata{}, Results: results, Report: types.Report{ Results: results, @@ -274,6 +283,10 @@ func vulnerabilitiesOrSecretResource(resource Resource) bool { return len(resource.Results) > 0 && (len(resource.Results[0].Vulnerabilities) > 0 || len(resource.Results[0].Secrets) > 0) } +func misconfigsResource(resource Resource) bool { + return len(resource.Results) > 0 && len(resource.Results[0].Misconfigurations) > 0 +} + func nodeKind(resource Resource) Resource { if nodeInfoResource(resource) { resource.Kind = "Node" diff --git a/pkg/k8s/report/report_test.go b/pkg/k8s/report/report_test.go index e9e9ae5e3a91..9ba663dc4783 100644 --- a/pkg/k8s/report/report_test.go +++ b/pkg/k8s/report/report_test.go @@ -14,12 +14,15 @@ var ( Namespace: "default", Kind: "Deploy", Name: "orion", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -69,12 +72,15 @@ var ( Namespace: "default", Kind: "Deploy", Name: "orion", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -113,16 +119,134 @@ var ( }, } - deployOrionWithBothVulnsAndMisconfigs = Resource{ + orionDeployWithAnotherMisconfig = Resource{ Namespace: "default", Kind: "Deploy", Name: "orion", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", + Results: types.Results{ + { + Misconfigurations: []types.DetectedMisconfiguration{ + { + ID: "ID201", + Status: types.MisconfStatusFailure, + Severity: "HIGH", + }, + }, + }, + }, + } + + image1WithVulns = Resource{ + Namespace: "default", + Kind: "Pod", + Name: "multi-image-pod", + Metadata: []types.Metadata{ + { + ImageID: "image1", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, + }, + }, + Results: types.Results{ + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-1111", + Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, + }, + }, + }, + }, + } + + image2WithVulns = Resource{ + Namespace: "default", + Kind: "Pod", + Name: "multi-image-pod", + Metadata: []types.Metadata{ + { + ImageID: "image2", + RepoTags: []string{ + "alpine:3.17.3", + }, + RepoDigests: []string{ + "alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126", + }, + }, + }, + Results: types.Results{ + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-2222", + Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, + }, + }, + }, + }, + } + + multiImagePodWithVulns = Resource{ + Namespace: "default", + Kind: "Pod", + Name: "multi-image-pod", + Metadata: []types.Metadata{ + { + ImageID: "image1", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + { + ImageID: "image2", + RepoTags: []string{ + "alpine:3.17.3", + }, + RepoDigests: []string{ + "alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126", + }, + }, + }, + Results: types.Results{ + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-1111", + Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, + }, + }, + }, + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-2222", + Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, + }, + }, + }, + }, + } + + deployOrionWithBothVulnsAndMisconfigs = Resource{ + Namespace: "default", + Kind: "Deploy", + Name: "orion", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -204,12 +328,15 @@ var ( Namespace: "default", Kind: "Cronjob", Name: "hello", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -221,12 +348,15 @@ var ( Namespace: "default", Kind: "Pod", Name: "prometheus", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -311,6 +441,10 @@ var ( ) func TestReport_consolidate(t *testing.T) { + concatenatedResource := orionDeployWithAnotherMisconfig + concatenatedResource.Results[0].Misconfigurations = append(concatenatedResource.Results[0].Misconfigurations, + deployOrionWithMisconfigs.Results[0].Misconfigurations...) + tests := []struct { name string report Report @@ -358,6 +492,30 @@ func TestReport_consolidate(t *testing.T) { "default/cronjob/hello": cronjobHelloWithVulns, }, }, + { + name: "report with misconfigs in image and pod", + report: Report{ + Resources: []Resource{ + deployOrionWithMisconfigs, + orionDeployWithAnotherMisconfig, + }, + }, + expectedFindings: map[string]Resource{ + "default/deploy/orion": concatenatedResource, + }, + }, + { + name: "report with multi image pod containing vulnerabilities", + report: Report{ + Resources: []Resource{ + image1WithVulns, + image2WithVulns, + }, + }, + expectedFindings: map[string]Resource{ + "default/pod/multi-image-pod": multiImagePodWithVulns, + }, + }, } for _, tt := range tests { diff --git a/pkg/k8s/scanner/io.go b/pkg/k8s/scanner/io.go index 9c32699ddd63..65f102a4b08c 100644 --- a/pkg/k8s/scanner/io.go +++ b/pkg/k8s/scanner/io.go @@ -3,6 +3,7 @@ package scanner import ( "fmt" "os" + "path/filepath" "regexp" "runtime" @@ -13,36 +14,63 @@ import ( "github.com/aquasecurity/trivy/pkg/log" ) -var r = regexp.MustCompile("\\\\|/|:|\\*|\\?|<|>") +var r = regexp.MustCompile("[\\\\/:*?<>]") -func createTempFile(artifact *artifacts.Artifact) (string, error) { +func generateTempFileByArtifact(artifact *artifacts.Artifact, tempDir string) (string, error) { filename := fmt.Sprintf("%s-%s-%s-*.yaml", artifact.Namespace, artifact.Kind, artifact.Name) - if runtime.GOOS == "windows" { // removes characters not permitted in file/directory names on Windows filename = filenameWindowsFriendly(filename) } - file, err := os.CreateTemp("", filename) + file, err := os.CreateTemp(tempDir, filename) if err != nil { - return "", xerrors.Errorf("creating tmp file error: %w", err) + return "", xerrors.Errorf("failed to create temporary file: %w", err) } + shouldRemove := false defer func() { if err := file.Close(); err != nil { - log.Error("Failed to close temp file", log.String("path", file.Name()), log.Err(err)) + log.Error("Failed to close temp file", log.FilePath(file.Name()), log.Err(err)) + } + if shouldRemove { + removeFile(file.Name()) } }() - if err := yaml.NewEncoder(file).Encode(artifact.RawResource); err != nil { - removeFile(filename) - return "", xerrors.Errorf("marshaling resource error: %w", err) + shouldRemove = true + return "", xerrors.Errorf("failed to encode artifact: %w", err) + } + return filepath.Base(file.Name()), nil +} + +// generateTempDir creates a directory with yaml files generated from kubernetes artifacts +// returns a directory name, a map for mapping a temp target file to k8s artifact and error +func generateTempDir(arts []*artifacts.Artifact) (string, map[string]*artifacts.Artifact, error) { + tempDir, err := os.MkdirTemp("", "trivyk8s*") + if err != nil { + return "", nil, xerrors.Errorf("failed to create temp directory: %w", err) + } + + m := make(map[string]*artifacts.Artifact) + for _, artifact := range arts { + filename, err := generateTempFileByArtifact(artifact, tempDir) + if err != nil { + log.Error("Failed to create temp file", log.FilePath(filename), log.Err(err)) + continue + } + m[filename] = artifact } + return tempDir, m, nil +} - return file.Name(), nil +func removeDir(dirname string) { + if err := os.RemoveAll(dirname); err != nil { + log.Error("Failed to remove temp directory", log.FilePath(dirname), log.Err(err)) + } } func removeFile(filename string) { if err := os.Remove(filename); err != nil { - log.Error("Failed to remove temp file", log.String("path", filename), log.Err(err)) + log.Error("Failed to remove temp file", log.FilePath(filename), log.Err(err)) } } diff --git a/pkg/k8s/scanner/io_test.go b/pkg/k8s/scanner/io_test.go index 7587d1bb8282..9e256b39f2b3 100644 --- a/pkg/k8s/scanner/io_test.go +++ b/pkg/k8s/scanner/io_test.go @@ -23,6 +23,11 @@ func Test_FilenameWindowsFriendly(t *testing.T) { fileName: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`, want: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`, }, + { + name: "name with no invalid - slash", + fileName: "-ClusterRoleBinding-system\\basic-user-725844313.yaml", + want: `-ClusterRoleBinding-system_basic-user-725844313.yaml`, + }, } for _, test := range tests { diff --git a/pkg/k8s/scanner/scanner.go b/pkg/k8s/scanner/scanner.go index 67d06b4c54bd..70debfe6a85f 100644 --- a/pkg/k8s/scanner/scanner.go +++ b/pkg/k8s/scanner/scanner.go @@ -82,14 +82,18 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact) var resources []report.Resource - type scanResult struct { - vulns []report.Resource - misconfig report.Resource + // scans kubernetes artifacts as a scope of yaml files + if local.ShouldScanMisconfigOrRbac(s.opts.Scanners) { + misconfigs, err := s.scanMisconfigs(ctx, resourceArtifacts) + if err != nil { + return report.Report{}, xerrors.Errorf("scanning misconfigurations error: %w", err) + } + resources = append(resources, misconfigs...) } - onItem := func(ctx context.Context, artifact *artifacts.Artifact) (scanResult, error) { - scanResults := scanResult{} - if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner, types.SecretScanner) && !s.opts.SkipImages { + // scan images from kubernetes cluster in parallel + if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner, types.SecretScanner) && !s.opts.SkipImages { + onItem := func(ctx context.Context, artifact *artifacts.Artifact) ([]report.Resource, error) { opts := s.opts opts.Credentials = make([]ftypes.Credential, len(s.opts.Credentials)) copy(opts.Credentials, s.opts.Credentials) @@ -106,33 +110,22 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact) } vulns, err := s.scanVulns(ctx, artifact, opts) if err != nil { - return scanResult{}, xerrors.Errorf("scanning vulnerabilities error: %w", err) + return nil, xerrors.Errorf("scanning vulnerabilities error: %w", err) } - scanResults.vulns = vulns + return vulns, nil } - if local.ShouldScanMisconfigOrRbac(s.opts.Scanners) { - misconfig, err := s.scanMisconfigs(ctx, artifact) - if err != nil { - return scanResult{}, xerrors.Errorf("scanning misconfigurations error: %w", err) - } - scanResults.misconfig = misconfig + + onResult := func(result []report.Resource) error { + resources = append(resources, result...) + return nil } - return scanResults, nil - } - onResult := func(result scanResult) error { - resources = append(resources, result.vulns...) - // don't add empty misconfig results to resources slice to avoid an empty resource - if result.misconfig.Results != nil { - resources = append(resources, result.misconfig) + p := parallel.NewPipeline(s.opts.Parallel, !s.opts.Quiet, resourceArtifacts, onItem, onResult) + if err := p.Do(ctx); err != nil { + return report.Report{}, err } - return nil } - p := parallel.NewPipeline(s.opts.Parallel, !s.opts.Quiet, resourceArtifacts, onItem, onResult) - if err := p.Do(ctx); err != nil { - return report.Report{}, err - } if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner) { k8sResource, err := s.scanK8sVulns(ctx, k8sCoreArtifacts) if err != nil { @@ -173,22 +166,43 @@ func (s *Scanner) scanVulns(ctx context.Context, artifact *artifacts.Artifact, o return resources, nil } -func (s *Scanner) scanMisconfigs(ctx context.Context, artifact *artifacts.Artifact) (report.Resource, error) { - configFile, err := createTempFile(artifact) +func (s *Scanner) scanMisconfigs(ctx context.Context, k8sArtifacts []*artifacts.Artifact) ([]report.Resource, error) { + dir, artifactsByFilename, err := generateTempDir(k8sArtifacts) if err != nil { - return report.Resource{}, xerrors.Errorf("scan error: %w", err) + return nil, xerrors.Errorf("failed to generate temp dir: %w", err) } - s.opts.Target = configFile + s.opts.Target = dir configReport, err := s.runner.ScanFilesystem(ctx, s.opts) - // remove config file after scanning - removeFile(configFile) + // remove config files after scanning + removeDir(dir) + if err != nil { - return report.CreateResource(artifact, configReport, err), err + return nil, xerrors.Errorf("failed to scan filesystem: %w", err) + } + resources := make([]report.Resource, 0, len(k8sArtifacts)) + + for _, res := range configReport.Results { + artifact := artifactsByFilename[res.Target] + + singleReport := types.Report{ + SchemaVersion: configReport.SchemaVersion, + CreatedAt: configReport.CreatedAt, + ArtifactName: res.Target, + ArtifactType: configReport.ArtifactType, + Metadata: configReport.Metadata, + Results: types.Results{res}, + } + + resource, err := s.filter(ctx, singleReport, artifact) + if err != nil { + resource = report.CreateResource(artifact, singleReport, err) + } + resources = append(resources, resource) } - return s.filter(ctx, configReport, artifact) + return resources, nil } func (s *Scanner) filter(ctx context.Context, r types.Report, artifact *artifacts.Artifact) (report.Resource, error) { var err error diff --git a/pkg/licensing/normalize.go b/pkg/licensing/normalize.go index 4ce294275214..bce81e68903d 100644 --- a/pkg/licensing/normalize.go +++ b/pkg/licensing/normalize.go @@ -626,7 +626,7 @@ func TrimLicenseText(text string) string { } // version number match -var versionRegexpString = "([A-UW-Z)]{2,})( LICENSE)?\\s*[,(-]?\\s*(V|V\\.|VER|VER\\.|VERSION|VERSION-|-)?\\s*([1-9](\\.\\d)*)[)]?" +var versionRegexpString = "([A-UW-Z)])( LICENSE)?\\s*[,(-]?\\s*(V|V\\.|VER|VER\\.|VERSION|VERSION-|-)?\\s*([1-9](\\.\\d)*)[)]?" // case insensitive version match anywhere in string var versionRegexp = regexp.MustCompile("(?i)" + versionRegexpString) diff --git a/pkg/licensing/normalize_test.go b/pkg/licensing/normalize_test.go index 9b47fd923f29..7daf7b57d783 100644 --- a/pkg/licensing/normalize_test.go +++ b/pkg/licensing/normalize_test.go @@ -209,6 +209,13 @@ func TestNormalize(t *testing.T) { normalized: " The unmapped license ", normalizedKey: " The unmapped license ", }, + { + licenses: []string{ + "Universal Permissive License, Version 1.0", + }, + normalized: "UPL-1.0", + normalizedKey: "UPL-1.0", + }, } for _, tt := range tests { t.Run(tt.normalized, func(t *testing.T) { diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index 7bec3ed6fe14..1aa2a5cd5c16 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -24,14 +24,13 @@ import ( cfscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation" cfparser "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" dfscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" "github.com/aquasecurity/trivy/pkg/iac/scanners/helm" - jsonscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/json" k8sscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes" "github.com/aquasecurity/trivy/pkg/iac/scanners/options" "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform" tfprawscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/snapshot" tfpjsonscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/tfjson" - yamlscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/yaml" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/mapfs" @@ -51,6 +50,12 @@ var enablediacTypes = map[detection.FileType]types.ConfigType{ detection.FileTypeYAML: types.YAML, } +type DisabledCheck struct { + ID string + Scanner string // For logging + Reason string // For logging +} + type ScannerOption struct { Trace bool RegoOnly bool @@ -75,9 +80,9 @@ type ScannerOption struct { FilePatterns []string ConfigFileSchemas []*ConfigFileSchema - DisabledCheckIDs []string - SkipFiles []string - SkipDirs []string + DisabledChecks []DisabledCheck + SkipFiles []string + SkipDirs []string } func (o *ScannerOption) Sort() { @@ -118,9 +123,9 @@ func NewScanner(t detection.FileType, opt ScannerOption) (*Scanner, error) { case detection.FileTypeTerraformPlanSnapshot: scanner = tfprawscanner.New(opts...) case detection.FileTypeYAML: - scanner = yamlscanner.NewScanner(opts...) + scanner = generic.NewYamlScanner(opts...) case detection.FileTypeJSON: - scanner = jsonscanner.NewScanner(opts...) + scanner = generic.NewJsonScanner(opts...) default: return nil, xerrors.Errorf("unknown file type: %s", t) } @@ -134,6 +139,7 @@ func NewScanner(t detection.FileType, opt ScannerOption) (*Scanner, error) { } func (s *Scanner) Scan(ctx context.Context, fsys fs.FS) ([]types.Misconfiguration, error) { + ctx = log.WithContextPrefix(ctx, log.PrefixMisconfiguration) newfs, err := s.filterFS(fsys) if err != nil { return nil, xerrors.Errorf("fs filter error: %w", err) @@ -142,12 +148,12 @@ func (s *Scanner) Scan(ctx context.Context, fsys fs.FS) ([]types.Misconfiguratio return nil, nil } - log.Debug("Scanning files for misconfigurations...", log.String("scanner", s.scanner.Name())) + log.DebugContext(ctx, "Scanning files for misconfigurations...", log.String("scanner", s.scanner.Name())) results, err := s.scanner.ScanFS(ctx, newfs, ".") if err != nil { var invalidContentError *cfparser.InvalidContentError if errors.As(err, &invalidContentError) { - log.Error("scan was broken with InvalidContentError", s.scanner.Name(), log.Err(err)) + log.ErrorContext(ctx, "scan was broken with InvalidContentError", s.scanner.Name(), log.Err(err)) return nil, nil } return nil, xerrors.Errorf("scan config error: %w", err) @@ -212,11 +218,17 @@ func (s *Scanner) filterFS(fsys fs.FS) (fs.FS, error) { } func scannerOptions(t detection.FileType, opt ScannerOption) ([]options.ScannerOption, error) { + disabledCheckIDs := lo.Map(opt.DisabledChecks, func(check DisabledCheck, _ int) string { + log.Info("Check disabled", log.Prefix(log.PrefixMisconfiguration), log.String("ID", check.ID), + log.String("scanner", check.Scanner), log.String("reason", check.Reason)) + return check.ID + }) + opts := []options.ScannerOption{ rego.WithEmbeddedPolicies(!opt.DisableEmbeddedPolicies), rego.WithEmbeddedLibraries(!opt.DisableEmbeddedLibraries), options.ScannerWithIncludeDeprecatedChecks(opt.IncludeDeprecatedChecks), - rego.WithDisabledCheckIDs(opt.DisabledCheckIDs...), + rego.WithDisabledCheckIDs(disabledCheckIDs...), } policyFS, policyPaths, err := CreatePolicyFS(opt.PolicyPaths) @@ -476,8 +488,6 @@ func ResultsToMisconf(configType types.ConfigType, scannerName string, results s switch flattened.Status { case scan.StatusPassed: misconf.Successes = append(misconf.Successes, misconfResult) - case scan.StatusIgnored: - misconf.Exceptions = append(misconf.Exceptions, misconfResult) case scan.StatusFailed: misconf.Failures = append(misconf.Failures, misconfResult) } diff --git a/pkg/oci/artifact.go b/pkg/oci/artifact.go index 416952578918..8ed7dcdad03d 100644 --- a/pkg/oci/artifact.go +++ b/pkg/oci/artifact.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/hashicorp/go-multierror" "github.com/samber/lo" "golang.org/x/xerrors" @@ -224,6 +225,7 @@ func NewArtifacts(repos []name.Reference, opt types.RegistryOptions, opts ...Opt // Download downloads artifacts until one of them succeeds. // Attempts to download next artifact if the first one fails due to a temporary error. func (a Artifacts) Download(ctx context.Context, dst string, opt DownloadOption) error { + var errs error for i, art := range a { log.InfoContext(ctx, "Downloading artifact...", log.String("repo", art.repository)) err := art.Download(ctx, dst, opt) @@ -239,9 +241,10 @@ func (a Artifacts) Download(ctx context.Context, dst string, opt DownloadOption) if i < len(a)-1 { log.InfoContext(ctx, "Trying to download artifact from other repository...") } + errs = multierror.Append(errs, err) } - return xerrors.New("failed to download artifact from any source") + return xerrors.Errorf("failed to download artifact from any source: %w", errs) } func shouldTryOtherRepo(err error) bool { diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index 25e9e7829d7b..d5ba15300393 100644 --- a/pkg/purl/purl_test.go +++ b/pkg/purl/purl_test.go @@ -809,38 +809,38 @@ func TestPackageURL_Match(t *testing.T) { }{ { name: "same purl", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", want: true, }, { name: "different type", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", target: "pkg:maven/github.com/aquasecurity/trivy@0.49.0", want: false, }, { name: "different namespace", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity2/trivy@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity2/trivy@v.49.0", want: false, }, { name: "different name", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity/tracee@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity/tracee@v0.49.0", want: false, }, { name: "different version", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity/trivy@0.49.1", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity/trivy@v0.49.1", want: false, }, { name: "version wildcard", constraint: "pkg:golang/github.com/aquasecurity/trivy", - target: "pkg:golang/github.com/aquasecurity/trivy@0.50.0", + target: "pkg:golang/github.com/aquasecurity/trivy@v0.50.0", want: true, }, { diff --git a/pkg/report/sarif.go b/pkg/report/sarif.go index a94dcccb2c9b..3bc3344611e2 100644 --- a/pkg/report/sarif.go +++ b/pkg/report/sarif.go @@ -5,6 +5,7 @@ import ( "fmt" "html" "io" + "net/url" "path/filepath" "regexp" "strings" @@ -15,6 +16,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/artifact" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" ) @@ -61,9 +63,9 @@ type sarifData struct { helpMarkdown string resourceClass types.ResultClass severity string - url string + url *url.URL resultIndex int - artifactLocation string + artifactLocation *url.URL locationMessage string message string cvssScore string @@ -97,8 +99,8 @@ func (sw *SarifWriter) addSarifRule(data *sarifData) { "precision": "very-high", "security-severity": data.cvssScore, }) - if data.url != "" { - r.WithHelpURI(data.url) + if data.url != nil && data.url.String() != "" { + r.WithHelpURI(data.url.String()) } } @@ -109,7 +111,7 @@ func (sw *SarifWriter) addSarifResult(data *sarifData) { WithRuleIndex(data.resultIndex). WithMessage(sarif.NewTextMessage(data.message)). WithLevel(toSarifErrorLevel(data.severity)). - WithLocations(toSarifLocations(data.locations, data.artifactLocation, data.locationMessage)) + WithLocations(toSarifLocations(data.locations, data.artifactLocation.String(), data.locationMessage)) sw.run.AddResult(result) } @@ -163,9 +165,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: vuln.VulnerabilityID, severity: vuln.Severity, cvssScore: getCVSSScore(vuln), - url: vuln.PrimaryURL, + url: toUri(vuln.PrimaryURL), resourceClass: res.Class, - artifactLocation: path, + artifactLocation: toUri(path), locationMessage: fmt.Sprintf("%v: %v@%v", path, vuln.PkgName, vuln.InstalledVersion), locations: sw.getLocations(vuln.PkgName, vuln.InstalledVersion, path, res.Packages), resultIndex: getRuleIndex(vuln.VulnerabilityID, ruleIndexes), @@ -186,9 +188,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: misconf.ID, severity: misconf.Severity, cvssScore: severityToScore(misconf.Severity), - url: misconf.PrimaryURL, + url: toUri(misconf.PrimaryURL), resourceClass: res.Class, - artifactLocation: locationURI, + artifactLocation: toUri(locationURI), locationMessage: locationURI, locations: []location{ { @@ -213,9 +215,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: secret.RuleID, severity: secret.Severity, cvssScore: severityToScore(secret.Severity), - url: builtinRulesUrl, + url: toUri(builtinRulesUrl), resourceClass: res.Class, - artifactLocation: target, + artifactLocation: toUri(target), locationMessage: target, locations: []location{ { @@ -242,9 +244,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: id, severity: license.Severity, cvssScore: severityToScore(license.Severity), - url: license.Link, + url: toUri(license.Link), resourceClass: res.Class, - artifactLocation: target, + artifactLocation: toUri(target), resultIndex: getRuleIndex(id, ruleIndexes), shortDescription: desc, fullDescription: desc, @@ -348,6 +350,15 @@ func clearURI(s string) string { return strings.ReplaceAll(strings.ReplaceAll(s, "\\", "/"), "git::https:/", "") } +func toUri(str string) *url.URL { + uri, err := url.Parse(str) + if err != nil { + logger := log.WithPrefix("sarif") + logger.Error("Unable to parse URI", log.String("URI", str), log.Err(err)) + } + return uri +} + func (sw *SarifWriter) getLocations(name, version, path string, pkgs []ftypes.Package) []location { id := fmt.Sprintf("%s@%s@%s", path, name, version) locs, ok := sw.locationCache[id] diff --git a/pkg/report/sarif_test.go b/pkg/report/sarif_test.go index cf4bfea8eb0b..ce68fab06a8a 100644 --- a/pkg/report/sarif_test.go +++ b/pkg/report/sarif_test.go @@ -41,7 +41,7 @@ func TestReportWriter_Sarif(t *testing.T) { }, Results: types.Results{ { - Target: "library/test", + Target: "library/test 1", Class: types.ClassOSPkg, Packages: []ftypes.Package{ { @@ -137,10 +137,10 @@ func TestReportWriter_Sarif(t *testing.T) { Message: sarif.Message{Text: lo.ToPtr("Package: foo\nInstalled Version: 1.2.3\nVulnerability CVE-2020-0001\nSeverity: HIGH\nFixed Version: 3.4.5\nLink: [CVE-2020-0001](https://avd.aquasec.com/nvd/cve-2020-0001)")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test: foo@1.2.3")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1: foo@1.2.3")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -152,10 +152,10 @@ func TestReportWriter_Sarif(t *testing.T) { }, }, { - Message: &sarif.Message{Text: lo.ToPtr("library/test: foo@1.2.3")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1: foo@1.2.3")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -192,7 +192,7 @@ func TestReportWriter_Sarif(t *testing.T) { input: types.Report{ Results: types.Results{ { - Target: "library/test", + Target: "library/test 1", Class: types.ClassConfig, Misconfigurations: []types.DetectedMisconfiguration{ { @@ -283,13 +283,13 @@ func TestReportWriter_Sarif(t *testing.T) { RuleID: lo.ToPtr("KSV001"), RuleIndex: lo.ToPtr[uint](0), Level: lo.ToPtr("error"), - Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test\nType: \nVulnerability KSV001\nSeverity: HIGH\nMessage: Message\nLink: [KSV001](https://avd.aquasec.com/appshield/ksv001)")}, + Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test 1\nType: \nVulnerability KSV001\nSeverity: HIGH\nMessage: Message\nLink: [KSV001](https://avd.aquasec.com/appshield/ksv001)")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -306,13 +306,13 @@ func TestReportWriter_Sarif(t *testing.T) { RuleID: lo.ToPtr("KSV002"), RuleIndex: lo.ToPtr[uint](1), Level: lo.ToPtr("error"), - Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test\nType: \nVulnerability KSV002\nSeverity: CRITICAL\nMessage: Message\nLink: [KSV002](https://avd.aquasec.com/appshield/ksv002)")}, + Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test 1\nType: \nVulnerability KSV002\nSeverity: CRITICAL\nMessage: Message\nLink: [KSV002](https://avd.aquasec.com/appshield/ksv002)")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -341,7 +341,7 @@ func TestReportWriter_Sarif(t *testing.T) { input: types.Report{ Results: types.Results{ { - Target: "library/test", + Target: "library/test 1", Class: types.ClassSecret, Secrets: []types.DetectedSecret{ { @@ -400,13 +400,13 @@ func TestReportWriter_Sarif(t *testing.T) { RuleID: lo.ToPtr("aws-secret-access-key"), RuleIndex: lo.ToPtr[uint](0), Level: lo.ToPtr("error"), - Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test\nType: \nSecret AWS Secret Access Key\nSeverity: CRITICAL\nMatch: 'AWS_secret_KEY'=\"****************************************\"")}, + Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test 1\nType: \nSecret AWS Secret Access Key\nSeverity: CRITICAL\nMatch: 'AWS_secret_KEY'=\"****************************************\"")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -495,7 +495,7 @@ func TestReportWriter_Sarif(t *testing.T) { Message: sarif.NewTextMessage(""), PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("OS Packages"), + URI: lo.ToPtr("OS%20Packages"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ diff --git a/pkg/report/table/misconfig.go b/pkg/report/table/misconfig.go index 112d783d0875..41f111d883c9 100644 --- a/pkg/report/table/misconfig.go +++ b/pkg/report/table/misconfig.go @@ -61,8 +61,8 @@ func (r *misconfigRenderer) Render() string { total, summaries := summarize(r.severities, r.countSeverities()) summary := r.result.MisconfSummary - r.printf("Tests: %d (SUCCESSES: %d, FAILURES: %d, EXCEPTIONS: %d)\n", - summary.Successes+summary.Failures+summary.Exceptions, summary.Successes, summary.Failures, summary.Exceptions) + r.printf("Tests: %d (SUCCESSES: %d, FAILURES: %d)\n", + summary.Successes+summary.Failures, summary.Successes, summary.Failures) r.printf("Failures: %d (%s)\n\n", total, strings.Join(summaries, ", ")) for _, m := range r.result.Misconfigurations { @@ -124,18 +124,18 @@ func (r *misconfigRenderer) renderSummary(misconf types.DetectedMisconfiguration } } - // severity + // ID & severity switch misconf.Severity { case severityCritical: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) case severityHigh: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) case severityMedium: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) case severityLow: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) default: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) } // heading diff --git a/pkg/report/table/misconfig_test.go b/pkg/report/table/misconfig_test.go index a57399f0e253..8d17798f673d 100644 --- a/pkg/report/table/misconfig_test.go +++ b/pkg/report/table/misconfig_test.go @@ -24,10 +24,11 @@ func TestMisconfigRenderer(t *testing.T) { name: "single result", input: types.Result{ Target: "my-file", - MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1, Exceptions: 0}, + MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1}, Misconfigurations: []types.DetectedMisconfiguration{ { - ID: "AVD-XYZ-0123", + ID: "some-alias-for-a-check", + AVDID: "AVD-XYZ-0123", Title: "Config file is bad", Description: "Your config file is not good.", Message: "Oh no, a bad config.", @@ -41,10 +42,10 @@ func TestMisconfigRenderer(t *testing.T) { want: ` my-file () ========== -Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) +Tests: 1 (SUCCESSES: 0, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) -HIGH: Oh no, a bad config. +AVD-XYZ-0123 (HIGH): Oh no, a bad config. ════════════════════════════════════════ Your config file is not good. @@ -58,10 +59,10 @@ See https://google.com/search?q=bad%20config name: "single result with code", input: types.Result{ Target: "my-file", - MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1, Exceptions: 0}, + MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1}, Misconfigurations: []types.DetectedMisconfiguration{ { - ID: "AVD-XYZ-0123", + AVDID: "AVD-XYZ-0123", Title: "Config file is bad", Description: "Your config file is not good.", Message: "Oh no, a bad config.", @@ -100,10 +101,10 @@ See https://google.com/search?q=bad%20config want: ` my-file () ========== -Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) +Tests: 1 (SUCCESSES: 0, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) -HIGH: Oh no, a bad config. +AVD-XYZ-0123 (HIGH): Oh no, a bad config. ════════════════════════════════════════ Your config file is not good. @@ -123,10 +124,10 @@ See https://google.com/search?q=bad%20config name: "multiple results", input: types.Result{ Target: "my-file", - MisconfSummary: &types.MisconfSummary{Successes: 1, Failures: 1, Exceptions: 0}, + MisconfSummary: &types.MisconfSummary{Successes: 1, Failures: 1}, Misconfigurations: []types.DetectedMisconfiguration{ { - ID: "AVD-XYZ-0123", + AVDID: "AVD-XYZ-0123", Title: "Config file is bad", Description: "Your config file is not good.", Message: "Oh no, a bad config.", @@ -157,7 +158,7 @@ See https://google.com/search?q=bad%20config }, }, { - ID: "AVD-XYZ-0456", + AVDID: "AVD-XYZ-0456", Title: "Config file is bad again", Description: "Your config file is still not good.", Message: "Oh no, a bad config AGAIN.", @@ -171,10 +172,10 @@ See https://google.com/search?q=bad%20config want: ` my-file () ========== -Tests: 2 (SUCCESSES: 1, FAILURES: 1, EXCEPTIONS: 0) +Tests: 2 (SUCCESSES: 1, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) -FAIL: HIGH: Oh no, a bad config. +FAIL: AVD-XYZ-0123 (HIGH): Oh no, a bad config. ════════════════════════════════════════ Your config file is not good. @@ -188,7 +189,7 @@ See https://google.com/search?q=bad%20config ──────────────────────────────────────── -PASS: MEDIUM: Oh no, a bad config AGAIN. +PASS: AVD-XYZ-0456 (MEDIUM): Oh no, a bad config AGAIN. ════════════════════════════════════════ Your config file is still not good. @@ -205,15 +206,14 @@ See https://google.com/search?q=bad%20config Class: types.ClassConfig, Type: "terraform", MisconfSummary: &types.MisconfSummary{ - Successes: 5, - Failures: 1, - Exceptions: 0, + Successes: 5, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ { Type: "Terraform Security Check", ID: "AVD-AWS-0107", - AVDID: "AVS-AWS-0107", + AVDID: "AVD-AWS-0107", Title: "An ingress security group rule allows traffic from /0", Description: "Opening up ports to the public internet is generally to be avoided. You should restrict access to IP addresses or ranges that explicitly require it where possible.", Message: "Security group rule allows ingress from public internet.", @@ -309,10 +309,10 @@ See https://google.com/search?q=bad%20config want: ` terraform-aws-modules/security-group/aws/main.tf (terraform) ============================================================ -Tests: 6 (SUCCESSES: 5, FAILURES: 1, EXCEPTIONS: 0) +Tests: 6 (SUCCESSES: 5, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1) -CRITICAL: Security group rule allows ingress from public internet. +AVD-AWS-0107 (CRITICAL): Security group rule allows ingress from public internet. ════════════════════════════════════════ Opening up ports to the public internet is generally to be avoided. You should restrict access to IP addresses or ranges that explicitly require it where possible. diff --git a/pkg/report/writer.go b/pkg/report/writer.go index a5b7ae231599..f25d579d66ef 100644 --- a/pkg/report/writer.go +++ b/pkg/report/writer.go @@ -2,10 +2,10 @@ package report import ( "context" - "errors" "io" "strings" + "github.com/hashicorp/go-multierror" "golang.org/x/xerrors" cr "github.com/aquasecurity/trivy/pkg/compliance/report" @@ -32,7 +32,7 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e } defer func() { if cerr := cleanup(); cerr != nil { - err = errors.Join(err, cerr) + err = multierror.Append(err, cerr) } }() diff --git a/pkg/result/filter.go b/pkg/result/filter.go index e1f0e632197e..83e0c0a7170e 100644 --- a/pkg/result/filter.go +++ b/pkg/result/filter.go @@ -130,13 +130,12 @@ func filterMisconfigurations(result *types.Result, severities []string, includeN // Filter by ignore file if f := ignoreConfig.MatchMisconfiguration(misconf.ID, misconf.AVDID, result.Target); f != nil { - result.MisconfSummary.Exceptions++ result.ModifiedFindings = append(result.ModifiedFindings, types.NewModifiedFinding(misconf, types.FindingStatusIgnored, f.Statement, ignoreConfig.FilePath)) continue } - // Count successes, failures, and exceptions + // Count successes and failures summarize(misconf.Status, result.MisconfSummary) if misconf.Status != types.MisconfStatusFailure && !includeNonFailures { @@ -210,8 +209,6 @@ func summarize(status types.MisconfStatus, summary *types.MisconfSummary) { summary.Failures++ case types.MisconfStatusPassed: summary.Successes++ - case types.MisconfStatusException: - summary.Exceptions++ } } @@ -256,7 +253,6 @@ func applyPolicy(ctx context.Context, result *types.Result, policyFile string) e return err } if ignored { - result.MisconfSummary.Exceptions++ switch misconf.Status { case types.MisconfStatusFailure: result.MisconfSummary.Failures-- diff --git a/pkg/result/filter_test.go b/pkg/result/filter_test.go index 0298cd0d9582..08b523dd2dde 100644 --- a/pkg/result/filter_test.go +++ b/pkg/result/filter_test.go @@ -21,16 +21,16 @@ import ( func TestFilter(t *testing.T) { var ( pkg1 = ftypes.Package{ - ID: "foo@1.2.3", + ID: "foo@v1.2.3", Name: "foo", - Version: "1.2.3", + Version: "v1.2.3", Identifier: ftypes.PkgIdentifier{ UID: "01", PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "foo", - Version: "1.2.3", + Version: "v1.2.3", }, }, } @@ -90,14 +90,14 @@ func TestFilter(t *testing.T) { vuln6 = types.DetectedVulnerability{ VulnerabilityID: "CVE-2019-0006", PkgName: "foo", - InstalledVersion: "1.2.3", + InstalledVersion: "v1.2.3", FixedVersion: "1.2.4", PkgIdentifier: ftypes.PkgIdentifier{ PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "foo", - Version: "1.2.3", + Version: "v1.2.3", }, }, Vulnerability: dbTypes.Vulnerability{ @@ -107,14 +107,14 @@ func TestFilter(t *testing.T) { vuln7 = types.DetectedVulnerability{ VulnerabilityID: "CVE-2019-0007", PkgName: "bar", - InstalledVersion: "2.3.4", + InstalledVersion: "v2.3.4", FixedVersion: "2.3.5", PkgIdentifier: ftypes.PkgIdentifier{ PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "bar", - Version: "2.3.4", + Version: "v2.3.4", }, }, Vulnerability: dbTypes.Vulnerability{ @@ -233,9 +233,8 @@ func TestFilter(t *testing.T) { vuln2, }, MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 0, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf1, @@ -403,9 +402,8 @@ func TestFilter(t *testing.T) { Target: "deployment.yaml", Class: types.ClassConfig, MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 1, - Exceptions: 1, + Successes: 1, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf1, @@ -522,9 +520,8 @@ func TestFilter(t *testing.T) { { Target: "app/Dockerfile", MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 2, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf3, @@ -641,9 +638,8 @@ func TestFilter(t *testing.T) { Results: types.Results{ { MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 1, - Exceptions: 1, + Successes: 1, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf1, diff --git a/pkg/result/testdata/openvex.json b/pkg/result/testdata/openvex.json index dcdd344700c7..385f22e47deb 100644 --- a/pkg/result/testdata/openvex.json +++ b/pkg/result/testdata/openvex.json @@ -8,7 +8,7 @@ { "vulnerability": {"name": "CVE-2019-0001"}, "products": [ - {"@id": "pkg:golang/github.com/aquasecurity/foo@1.2.3"} + {"@id": "pkg:golang/github.com/aquasecurity/foo@v1.2.3"} ], "status": "not_affected", "justification": "vulnerable_code_not_in_execute_path" diff --git a/pkg/rpc/convert.go b/pkg/rpc/convert.go index 1662877bd597..89097730111b 100644 --- a/pkg/rpc/convert.go +++ b/pkg/rpc/convert.go @@ -754,13 +754,12 @@ func ConvertFromRPCMisconfigurations(rpcMisconfs []*common.Misconfiguration) []f var misconfs []ftypes.Misconfiguration for _, rpcMisconf := range rpcMisconfs { misconfs = append(misconfs, ftypes.Misconfiguration{ - FileType: ftypes.ConfigType(rpcMisconf.FileType), - FilePath: rpcMisconf.FilePath, - Successes: ConvertFromRPCMisconfResults(rpcMisconf.Successes), - Warnings: ConvertFromRPCMisconfResults(rpcMisconf.Warnings), - Failures: ConvertFromRPCMisconfResults(rpcMisconf.Failures), - Exceptions: ConvertFromRPCMisconfResults(rpcMisconf.Exceptions), - Layer: ftypes.Layer{}, + FileType: ftypes.ConfigType(rpcMisconf.FileType), + FilePath: rpcMisconf.FilePath, + Successes: ConvertFromRPCMisconfResults(rpcMisconf.Successes), + Warnings: ConvertFromRPCMisconfResults(rpcMisconf.Warnings), + Failures: ConvertFromRPCMisconfResults(rpcMisconf.Failures), + Layer: ftypes.Layer{}, }) } return misconfs @@ -875,12 +874,11 @@ func ConvertToRPCPutBlobRequest(diffID string, blobInfo ftypes.BlobInfo) *cache. var misconfigurations []*common.Misconfiguration for _, m := range blobInfo.Misconfigurations { misconfigurations = append(misconfigurations, &common.Misconfiguration{ - FileType: string(m.FileType), - FilePath: m.FilePath, - Successes: ConvertToMisconfResults(m.Successes), - Warnings: ConvertToMisconfResults(m.Warnings), - Failures: ConvertToMisconfResults(m.Failures), - Exceptions: ConvertToMisconfResults(m.Exceptions), + FileType: string(m.FileType), + FilePath: m.FilePath, + Successes: ConvertToMisconfResults(m.Successes), + Warnings: ConvertToMisconfResults(m.Warnings), + Failures: ConvertToMisconfResults(m.Failures), }) } diff --git a/pkg/sbom/io/encode_test.go b/pkg/sbom/io/encode_test.go index 52fbed415933..5a5b821590b8 100644 --- a/pkg/sbom/io/encode_test.go +++ b/pkg/sbom/io/encode_test.go @@ -461,7 +461,7 @@ func TestEncoder_Encode(t *testing.T) { Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "direct", - Version: "1.0.0", + Version: "v1.0.0", }, }, Relationship: ftypes.RelationshipDirect, @@ -472,28 +472,28 @@ func TestEncoder_Encode(t *testing.T) { { ID: "github.com/org/indirect@v2.0.0", Name: "github.com/org/indirect", - Version: "2.0.0", + Version: "v2.0.0", Identifier: ftypes.PkgIdentifier{ UID: "955AB4E7E24AC085", PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "indirect", - Version: "2.0.0", + Version: "v2.0.0", }, }, Relationship: ftypes.RelationshipIndirect, }, { - ID: "stdlib@1.22.1", + ID: "stdlib@v1.22.1", Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", Identifier: ftypes.PkgIdentifier{ UID: "49728B9674E318A6", PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", }, }, Relationship: ftypes.RelationshipDirect, @@ -561,7 +561,7 @@ func TestEncoder_Encode(t *testing.T) { uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000004"): { Type: core.TypeLibrary, Name: "github.com/org/direct", - Version: "1.0.0", + Version: "v1.0.0", SrcFile: "test", Properties: []core.Property{ { @@ -579,15 +579,15 @@ func TestEncoder_Encode(t *testing.T) { Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "direct", - Version: "1.0.0", + Version: "v1.0.0", }, - BOMRef: "pkg:golang/github.com/org/direct@1.0.0", + BOMRef: "pkg:golang/github.com/org/direct@v1.0.0", }, }, uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000005"): { Type: core.TypeLibrary, Name: "github.com/org/indirect", - Version: "2.0.0", + Version: "v2.0.0", SrcFile: "test", Properties: []core.Property{ { @@ -605,20 +605,20 @@ func TestEncoder_Encode(t *testing.T) { Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "indirect", - Version: "2.0.0", + Version: "v2.0.0", }, - BOMRef: "pkg:golang/github.com/org/indirect@2.0.0", + BOMRef: "pkg:golang/github.com/org/indirect@v2.0.0", }, }, uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000006"): { Type: core.TypeLibrary, Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", SrcFile: "test", Properties: []core.Property{ { Name: core.PropertyPkgID, - Value: "stdlib@1.22.1", + Value: "stdlib@v1.22.1", }, { Name: core.PropertyPkgType, @@ -630,9 +630,9 @@ func TestEncoder_Encode(t *testing.T) { PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", }, - BOMRef: "pkg:golang/stdlib@1.22.1", + BOMRef: "pkg:golang/stdlib@v1.22.1", }, }, }, diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index 809ce95d7e4c..51f9144f682d 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -50,6 +50,8 @@ const ( PackageSupplierNoAssertion = "NOASSERTION" PackageSupplierOrganization = "Organization" + PackageAnnotatorToolField = "Tool" + RelationShipContains = common.TypeRelationshipContains RelationShipDescribe = common.TypeRelationshipDescribe RelationShipDependsOn = common.TypeRelationshipDependsOn @@ -122,6 +124,9 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, packages []*spdx.Package ) + // Lock time to use same time for all spdx fields + timeNow := clock.Now(ctx).UTC().Format(time.RFC3339) + root := bom.Root() pkgDownloadLocation := m.packageDownloadLocation(root) @@ -129,7 +134,7 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, packageIDs := make(map[uuid.UUID]spdx.ElementID) // Root package contains OS, OS packages, language-specific packages and so on. - rootPkg, err := m.rootSPDXPackage(root, pkgDownloadLocation) + rootPkg, err := m.rootSPDXPackage(root, timeNow, pkgDownloadLocation) if err != nil { return nil, xerrors.Errorf("failed to generate a root package: %w", err) } @@ -144,7 +149,7 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, if c.Root { continue } - spdxPackage, err := m.spdxPackage(c, pkgDownloadLocation) + spdxPackage, err := m.spdxPackage(c, timeNow, pkgDownloadLocation) if err != nil { return nil, xerrors.Errorf("spdx package error: %w", err) } @@ -216,7 +221,7 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, CreatorType: "Tool", }, }, - Created: clock.Now(ctx).UTC().Format(time.RFC3339), + Created: timeNow, }, Packages: packages, Relationships: relationShips, @@ -237,7 +242,7 @@ func (m *Marshaler) packageDownloadLocation(root *core.Component) string { return location } -func (m *Marshaler) rootSPDXPackage(root *core.Component, pkgDownloadLocation string) (*spdx.Package, error) { +func (m *Marshaler) rootSPDXPackage(root *core.Component, timeNow, pkgDownloadLocation string) (*spdx.Package, error) { var externalReferences []*spdx.PackageExternalReference // When the target is a container image, add PURL to the external references of the root package. if root.PkgIdentifier.PURL != nil { @@ -258,17 +263,25 @@ func (m *Marshaler) rootSPDXPackage(root *core.Component, pkgDownloadLocation st PackageName: root.Name, PackageSPDXIdentifier: elementID(camelCase(string(root.Type)), pkgID), PackageDownloadLocation: pkgDownloadLocation, - PackageAttributionTexts: m.spdxAttributionTexts(root), + Annotations: m.spdxAnnotations(root, timeNow), PackageExternalReferences: externalReferences, PrimaryPackagePurpose: pkgPurpose, }, nil } -func (m *Marshaler) appendAttributionText(attributionTexts []string, key, value string) []string { +func (m *Marshaler) appendAnnotation(annotations []spdx.Annotation, timeNow, key, value string) []spdx.Annotation { if value == "" { - return attributionTexts + return annotations } - return append(attributionTexts, fmt.Sprintf("%s: %s", key, value)) + return append(annotations, spdx.Annotation{ + AnnotationDate: timeNow, + AnnotationType: spdx.CategoryOther, + Annotator: spdx.Annotator{ + Annotator: fmt.Sprintf("%s-%s", CreatorTool, m.appVersion), + AnnotatorType: PackageAnnotatorToolField, + }, + AnnotationComment: fmt.Sprintf("%s: %s", key, value), + }) } func (m *Marshaler) purlExternalReference(packageURL string) *spdx.PackageExternalReference { @@ -287,7 +300,7 @@ func (m *Marshaler) advisoryExternalReference(primaryURL string) *spdx.PackageEx } } -func (m *Marshaler) spdxPackage(c *core.Component, pkgDownloadLocation string) (spdx.Package, error) { +func (m *Marshaler) spdxPackage(c *core.Component, timeNow, pkgDownloadLocation string) (spdx.Package, error) { pkgID, err := calcPkgID(m.hasher, c) if err != nil { return spdx.Package{}, xerrors.Errorf("failed to get os metadata package ID: %w", err) @@ -343,7 +356,7 @@ func (m *Marshaler) spdxPackage(c *core.Component, pkgDownloadLocation string) ( PrimaryPackagePurpose: purpose, PackageDownloadLocation: pkgDownloadLocation, PackageExternalReferences: pkgExtRefs, - PackageAttributionTexts: m.spdxAttributionTexts(c), + Annotations: m.spdxAnnotations(c, timeNow), PackageSourceInfo: sourceInfo, PackageSupplier: supplier, PackageChecksums: m.spdxChecksums(digests), @@ -365,16 +378,15 @@ func spdxPkgName(component *core.Component) string { } return component.Name } - -func (m *Marshaler) spdxAttributionTexts(c *core.Component) []string { - var texts []string +func (m *Marshaler) spdxAnnotations(c *core.Component, timeNow string) []spdx.Annotation { + var annotations []spdx.Annotation for _, p := range c.Properties { // Add properties that are not in other fields. if !slices.Contains(duplicateProperties, p.Name) { - texts = m.appendAttributionText(texts, p.Name, p.Value) + annotations = m.appendAnnotation(annotations, timeNow, p.Name, p.Value) } } - return texts + return annotations } func (m *Marshaler) spdxLicense(c *core.Component) string { diff --git a/pkg/sbom/spdx/marshal_test.go b/pkg/sbom/spdx/marshal_test.go index 74e9bee0a45d..4d9d33c013a7 100644 --- a/pkg/sbom/spdx/marshal_test.go +++ b/pkg/sbom/spdx/marshal_test.go @@ -25,6 +25,20 @@ import ( "github.com/aquasecurity/trivy/pkg/uuid" ) +func annotation(t *testing.T, comment string) spdx.Annotation { + t.Helper() + + return spdx.Annotation{ + AnnotationDate: "2021-08-25T12:20:30Z", + AnnotationType: spdx.CategoryOther, + Annotator: spdx.Annotator{ + Annotator: "trivy-0.56.2", + AnnotatorType: tspdx.PackageAnnotatorToolField, + }, + AnnotationComment: comment, + } +} + func TestMarshaler_Marshal(t *testing.T) { testCases := []struct { name string @@ -164,7 +178,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -176,9 +190,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "app/Gemfile.lock", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: bundler"), }, }, { @@ -186,9 +200,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "app/subproject/Gemfile.lock", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: bundler"), }, }, { @@ -202,14 +216,14 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:oci/rails@sha256%3Aa27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177?arch=arm64&repository_url=index.docker.io%2Flibrary%2Frails", }, }, - PackageAttributionTexts: []string{ - "DiffID: sha256:d871dadfb37b53ef1ca45be04fc527562b91989991a8f545345ae3be0b93f92a", - "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6", - "Labels:vendor: aquasecurity", - "RepoDigest: rails@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177", - "RepoTag: rails:latest", - "SchemaVersion: 2", - "Size: 1024", + Annotations: []spdx.Annotation{ + annotation(t, "DiffID: sha256:d871dadfb37b53ef1ca45be04fc527562b91989991a8f545345ae3be0b93f92a"), + annotation(t, "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6"), + annotation(t, "Labels:vendor: aquasecurity"), + annotation(t, "RepoDigest: rails@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177"), + annotation(t, "RepoTag: rails:latest"), + annotation(t, "SchemaVersion: 2"), + annotation(t, "Size: 1024"), }, PrimaryPackagePurpose: tspdx.PackagePurposeContainer, }, @@ -220,8 +234,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "7.0.1", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -241,8 +255,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "7.0.1", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -262,8 +276,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "7.0.1", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -283,8 +297,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "2.30-93.el8", PackageLicenseConcluded: "GPL-3.0-or-later", PackageLicenseDeclared: "GPL-3.0-or-later", - PackageAttributionTexts: []string{ - "PkgType: centos", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: centos"), }, PackageSupplier: &spdx.Supplier{ SupplierType: tspdx.PackageSupplierOrganization, @@ -312,9 +326,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "centos", PackageVersion: "8.3.2011", PrimaryPackagePurpose: tspdx.PackagePurposeOS, - PackageAttributionTexts: []string{ - "Class: os-pkgs", - "Type: centos", + Annotations: []spdx.Annotation{ + annotation(t, "Class: os-pkgs"), + annotation(t, "Type: centos"), }, }, }, @@ -486,7 +500,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -497,11 +511,11 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "centos:latest", PackageSPDXIdentifier: "ContainerImage-413bfede37ad01fc", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6", - "RepoTag: centos:latest", - "SchemaVersion: 2", - "Size: 1024", + Annotations: []spdx.Annotation{ + annotation(t, "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6"), + annotation(t, "RepoTag: centos:latest"), + annotation(t, "SchemaVersion: 2"), + annotation(t, "Size: 1024"), }, PrimaryPackagePurpose: tspdx.PackagePurposeContainer, }, @@ -512,8 +526,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "1:2.2.53-1.el8", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseDeclared: "GPL-2.0-or-later", - PackageAttributionTexts: []string{ - "PkgType: centos", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: centos"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -546,9 +560,9 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:gem/actionpack@7.0.1", }, }, - PackageAttributionTexts: []string{ - "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488", - "PkgType: gemspec", + Annotations: []spdx.Annotation{ + annotation(t, "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488"), + annotation(t, "PkgType: gemspec"), }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, @@ -571,9 +585,9 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:gem/actionpack@7.0.1", }, }, - PackageAttributionTexts: []string{ - "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488", - "PkgType: gemspec", + Annotations: []spdx.Annotation{ + annotation(t, "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488"), + annotation(t, "PkgType: gemspec"), }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, @@ -588,9 +602,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "centos", PackageVersion: "8.3.2011", PrimaryPackagePurpose: tspdx.PackagePurposeOS, - PackageAttributionTexts: []string{ - "Class: os-pkgs", - "Type: centos", + Annotations: []spdx.Annotation{ + annotation(t, "Class: os-pkgs"), + annotation(t, "Type: centos"), }, }, }, @@ -719,7 +733,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -731,9 +745,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "Gemfile.lock", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: bundler"), }, }, { @@ -741,9 +755,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "pom.xml", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: pom", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: pom"), }, }, { @@ -763,8 +777,8 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: Gemfile.lock", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, }, { @@ -784,17 +798,17 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: pom.xml", - PackageAttributionTexts: []string{ - "PkgID: com.example:example:1.0.0", - "PkgType: pom", + Annotations: []spdx.Annotation{ + annotation(t, "PkgID: com.example:example:1.0.0"), + annotation(t, "PkgType: pom"), }, }, { PackageSPDXIdentifier: spdx.ElementID("Filesystem-5af0f1f08c20909a"), PackageDownloadLocation: "NONE", PackageName: "masahiro331/CVE-2021-41098", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -878,7 +892,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -906,16 +920,16 @@ func TestMarshaler_Marshal(t *testing.T) { }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, - PackageAttributionTexts: []string{ - "PkgType: jar", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: jar"), }, }, { PackageSPDXIdentifier: spdx.ElementID("Filesystem-121e7e7a43f02ab"), PackageDownloadLocation: "NONE", PackageName: "log4j-core-2.17.0.jar", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -980,7 +994,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1001,9 +1015,9 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:npm/ruby-typeprof@0.20.1", }, }, - PackageAttributionTexts: []string{ - "LayerDiffID: sha256:661c3fd3cc16b34c070f3620ca6b03b6adac150f9a7e5d0e3c707a159990f88e", - "PkgType: node-pkg", + Annotations: []spdx.Annotation{ + annotation(t, "LayerDiffID: sha256:661c3fd3cc16b34c070f3620ca6b03b6adac150f9a7e5d0e3c707a159990f88e"), + annotation(t, "PkgType: node-pkg"), }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, @@ -1016,8 +1030,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageSPDXIdentifier: "Repository-1a78857c1a6a759e", PackageName: "http://test-aggregate", PackageDownloadLocation: "git+http://test-aggregate", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1075,7 +1089,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1086,8 +1100,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "empty/path", PackageSPDXIdentifier: "Filesystem-70f34983067dba86", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1137,7 +1151,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1148,8 +1162,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "secret", PackageSPDXIdentifier: "Filesystem-5c08d34162a2c5d3", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1209,7 +1223,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1221,9 +1235,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "/usr/local/bin/test", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: gobinary", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: gobinary"), }, }, { @@ -1235,8 +1249,8 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: /usr/local/bin/test", - PackageAttributionTexts: []string{ - "PkgType: gobinary", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: gobinary"), }, }, { @@ -1256,16 +1270,16 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: /usr/local/bin/test", - PackageAttributionTexts: []string{ - "PkgType: gobinary", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: gobinary"), }, }, { PackageName: "go-artifact", PackageSPDXIdentifier: "Filesystem-e340f27468b382be", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1326,7 +1340,7 @@ func TestMarshaler_Marshal(t *testing.T) { ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d") - marshaler := tspdx.NewMarshaler("0.38.1", tspdx.WithHasher(hasher)) + marshaler := tspdx.NewMarshaler("0.56.2", tspdx.WithHasher(hasher)) spdxDoc, err := marshaler.MarshalReport(ctx, tc.inputReport) require.NoError(t, err) diff --git a/pkg/sbom/spdx/testdata/happy/bom.json b/pkg/sbom/spdx/testdata/happy/bom.json index 823c0471351f..88af63b3b71a 100644 --- a/pkg/sbom/spdx/testdata/happy/bom.json +++ b/pkg/sbom/spdx/testdata/happy/bom.json @@ -37,13 +37,43 @@ }, { "SPDXID": "SPDXRef-ContainerImage-b5d81cde5f95c8fc", - "attributionTexts": [ - "SchemaVersion: 2", - "ImageID: sha256:49193a2310dbad4c02382da87ac624a80a92387a4f7536235f9ba590e5bcd7b5", - "DiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3", - "DiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1", - "RepoTag: maven-test-project:latest", - "RepoTag: tmp-test:latest" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "SchemaVersion: 2" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "ImageID: sha256:49193a2310dbad4c02382da87ac624a80a92387a4f7536235f9ba590e5bcd7b5" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "DiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "DiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "RepoTag: maven-test-project:latest" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "RepoTag: tmp-test:latest" + } ], "filesAnalyzed": false, "name": "meven-test-project" @@ -56,8 +86,13 @@ }, { "SPDXID": "SPDXRef-Package-2906575950df652b", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -74,8 +109,13 @@ }, { "SPDXID": "SPDXRef-Package-2a53baa495b9ddaf", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -92,8 +132,13 @@ }, { "SPDXID": "SPDXRef-Package-5e2e255ac76747ef", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -110,8 +155,13 @@ }, { "SPDXID": "SPDXRef-Package-5f1dbaff8de5eb06", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -128,8 +178,13 @@ }, { "SPDXID": "SPDXRef-Package-84ebffe38343d949", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -146,8 +201,13 @@ }, { "SPDXID": "SPDXRef-Package-b7ebaf0233f1ef7b", - "attributionTexts": [ - "LayerDiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3" + } ], "externalRefs": [ { diff --git a/pkg/sbom/spdx/unmarshal.go b/pkg/sbom/spdx/unmarshal.go index d2ba05b44999..c7ed6b0905dd 100644 --- a/pkg/sbom/spdx/unmarshal.go +++ b/pkg/sbom/spdx/unmarshal.go @@ -194,9 +194,21 @@ func (s *SPDX) parsePackage(spdxPkg spdx.Package) (*core.Component, error) { } } - // Attributions - for _, attr := range spdxPkg.PackageAttributionTexts { - k, v, ok := strings.Cut(attr, ": ") + // Trivy stores properties in Annotations + // But previous versions stored properties in AttributionTexts + // So we need to check both cases to maintain backward compatibility + var props []string + if len(spdxPkg.Annotations) > 0 { + for _, annotation := range spdxPkg.Annotations { + props = append(props, annotation.AnnotationComment) + } + } else if len(spdxPkg.PackageAttributionTexts) > 0 { + for _, attr := range spdxPkg.PackageAttributionTexts { + props = append(props, attr) + } + } + for _, prop := range props { + k, v, ok := strings.Cut(prop, ": ") if !ok { continue } diff --git a/pkg/scanner/local/scan.go b/pkg/scanner/local/scan.go index f34723058e06..58cd4cc00167 100644 --- a/pkg/scanner/local/scan.go +++ b/pkg/scanner/local/scan.go @@ -210,9 +210,6 @@ func (s Scanner) MisconfsToResults(misconfs []ftypes.Misconfiguration) types.Res for _, w := range misconf.Successes { detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.MisconfStatusPassed, misconf.Layer)) } - for _, w := range misconf.Exceptions { - detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.MisconfStatusException, misconf.Layer)) - } results = append(results, types.Result{ Target: misconf.FilePath, diff --git a/pkg/scanner/local/scan_test.go b/pkg/scanner/local/scan_test.go index f3e6cac5d3fa..bdeb5166a2fa 100644 --- a/pkg/scanner/local/scan_test.go +++ b/pkg/scanner/local/scan_test.go @@ -820,17 +820,6 @@ func TestScanner_Scan(t *testing.T) { }, }, }, - Exceptions: ftypes.MisconfResults{ - { - Namespace: "main.kubernetes.id100", - PolicyMetadata: ftypes.PolicyMetadata{ - ID: "ID100", - Type: "Kubernetes Security Check", - Title: "Bad Deployment", - Severity: "HIGH", - }, - }, - }, Layer: ftypes.Layer{ DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303", }, @@ -922,18 +911,6 @@ func TestScanner_Scan(t *testing.T) { DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303", }, }, - { - Type: "Kubernetes Security Check", - ID: "ID100", - Title: "Bad Deployment", - Message: "No issues found", - Namespace: "main.kubernetes.id100", - Severity: "HIGH", - Status: types.MisconfStatusException, - Layer: ftypes.Layer{ - DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303", - }, - }, }, }, }, diff --git a/pkg/types/report.go b/pkg/types/report.go index af364c95a11d..3ada68a942d8 100644 --- a/pkg/types/report.go +++ b/pkg/types/report.go @@ -130,13 +130,12 @@ func (r *Result) IsEmpty() bool { } type MisconfSummary struct { - Successes int - Failures int - Exceptions int + Successes int + Failures int } func (s MisconfSummary) Empty() bool { - return s.Successes == 0 && s.Failures == 0 && s.Exceptions == 0 + return s.Successes == 0 && s.Failures == 0 } // Failed returns whether the result includes any vulnerabilities, misconfigurations or secrets diff --git a/pkg/vex/repo/repo.go b/pkg/vex/repo/repo.go index 433b8224c20e..b9637c229ee9 100644 --- a/pkg/vex/repo/repo.go +++ b/pkg/vex/repo/repo.go @@ -11,6 +11,7 @@ import ( "time" "github.com/hashicorp/go-getter" + "github.com/hashicorp/go-multierror" "github.com/samber/lo" "golang.org/x/xerrors" @@ -256,7 +257,7 @@ func (r *Repository) download(ctx context.Context, ver Version, dst string, opts etag = etags[loc.URL] // Keep the old ETag // Update last updated time so that Trivy will not try to download the same URL soon case err != nil: - errs = errors.Join(errs, err) + errs = multierror.Append(errs, err) continue // Try the next location default: // Successfully downloaded diff --git a/pkg/vex/testdata/csaf-relationships.json b/pkg/vex/testdata/csaf-relationships.json index 2e823d17a2c6..18c311228206 100644 --- a/pkg/vex/testdata/csaf-relationships.json +++ b/pkg/vex/testdata/csaf-relationships.json @@ -42,7 +42,7 @@ "name": "go-direct1 v2.0.0", "product_id": "go-direct1-v2.0.0", "product_identification_helper": { - "purl": "pkg:golang/github.com/aquasecurity/go-direct1@2.0.0" + "purl": "pkg:golang/github.com/aquasecurity/go-direct1@v2.0.0" } } } @@ -65,7 +65,7 @@ "name": "go-transitive v4.0.0", "product_id": "go-transitive-v4.0.0", "product_identification_helper": { - "purl": "pkg:golang/github.com/aquasecurity/go-transitive@4.0.0" + "purl": "pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0" } } } diff --git a/pkg/vex/testdata/csaf.json b/pkg/vex/testdata/csaf.json index 70afefe70205..182856f96a51 100644 --- a/pkg/vex/testdata/csaf.json +++ b/pkg/vex/testdata/csaf.json @@ -50,7 +50,7 @@ "name": "go-transitive v4.0.0", "product_id": "go-transitive-v4.0.0", "product_identification_helper": { - "purl": "pkg:golang/github.com/aquasecurity/go-transitive@4.0.0" + "purl": "pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0" } } } diff --git a/pkg/vex/testdata/openvex-nested.json b/pkg/vex/testdata/openvex-nested.json index da7dd68615a5..637603d9e48c 100644 --- a/pkg/vex/testdata/openvex-nested.json +++ b/pkg/vex/testdata/openvex-nested.json @@ -11,10 +11,10 @@ }, "products": [ { - "@id": "pkg:golang/github.com/aquasecurity/go-direct1@2.0.0", + "@id": "pkg:golang/github.com/aquasecurity/go-direct1@v2.0.0", "subcomponents": [ { - "@id": "pkg:golang/github.com/aquasecurity/go-transitive@4.0.0" + "@id": "pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0" } ] } diff --git a/pkg/vex/vex_test.go b/pkg/vex/vex_test.go index 4a9686972a5e..bc7a5c069402 100644 --- a/pkg/vex/vex_test.go +++ b/pkg/vex/vex_test.go @@ -58,9 +58,9 @@ var ( }, } goModulePackage = ftypes.Package{ - ID: "github.com/aquasecurity/go-module@1.0.0", + ID: "github.com/aquasecurity/go-module@v1.0.0", Name: "github.com/aquasecurity/go-module", - Version: "1.0.0", + Version: "v1.0.0", Relationship: ftypes.RelationshipRoot, Identifier: ftypes.PkgIdentifier{ UID: "03", @@ -68,14 +68,14 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-module", - Version: "1.0.0", + Version: "v1.0.0", }, }, } goDirectPackage1 = ftypes.Package{ - ID: "github.com/aquasecurity/go-direct1@2.0.0", + ID: "github.com/aquasecurity/go-direct1@v2.0.0", Name: "github.com/aquasecurity/go-direct1", - Version: "2.0.0", + Version: "v2.0.0", Relationship: ftypes.RelationshipDirect, Identifier: ftypes.PkgIdentifier{ UID: "04", @@ -83,14 +83,14 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-direct1", - Version: "2.0.0", + Version: "v2.0.0", }, }, } goDirectPackage2 = ftypes.Package{ - ID: "github.com/aquasecurity/go-direct2@3.0.0", + ID: "github.com/aquasecurity/go-direct2@v3.0.0", Name: "github.com/aquasecurity/go-direct2", - Version: "3.0.0", + Version: "v3.0.0", Relationship: ftypes.RelationshipDirect, Identifier: ftypes.PkgIdentifier{ UID: "05", @@ -98,14 +98,14 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-direct2", - Version: "3.0.0", + Version: "v3.0.0", }, }, } goTransitivePackage = ftypes.Package{ - ID: "github.com/aquasecurity/go-transitive@4.0.0", + ID: "github.com/aquasecurity/go-transitive@v4.0.0", Name: "github.com/aquasecurity/go-transitive", - Version: "4.0.0", + Version: "v4.0.0", Relationship: ftypes.RelationshipIndirect, Identifier: ftypes.PkgIdentifier{ UID: "06", @@ -113,7 +113,7 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-transitive", - Version: "4.0.0", + Version: "v4.0.0", }, }, } @@ -646,9 +646,9 @@ func goSinglePathResult(result types.Result) types.Result { result.Type = ftypes.GoModule result.Class = types.ClassLangPkg - // - pkg:golang/github.com/aquasecurity/go-module@1.0.0 - // - pkg:golang/github.com/aquasecurity/go-direct1@2.0.0 - // - pkg:golang/github.com/aquasecurity/go-transitive@4.0.0 + // - pkg:golang/github.com/aquasecurity/go-module@v1.0.0 + // - pkg:golang/github.com/aquasecurity/go-direct1@v2.0.0 + // - pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0 goModule := clonePackage(goModulePackage) goDirect1 := clonePackage(goDirectPackage1) goTransitive := clonePackage(goTransitivePackage) @@ -667,11 +667,11 @@ func goMultiPathResult(result types.Result) types.Result { result.Type = ftypes.GoModule result.Class = types.ClassLangPkg - // - pkg:golang/github.com/aquasecurity/go-module@2.0.0 - // - pkg:golang/github.com/aquasecurity/go-direct1@3.0.0 - // - pkg:golang/github.com/aquasecurity/go-transitive@5.0.0 - // - pkg:golang/github.com/aquasecurity/go-direct2@4.0.0 - // - pkg:golang/github.com/aquasecurity/go-transitive@5.0.0 + // - pkg:golang/github.com/aquasecurity/go-module@v2.0.0 + // - pkg:golang/github.com/aquasecurity/go-direct1@v3.0.0 + // - pkg:golang/github.com/aquasecurity/go-transitive@v5.0.0 + // - pkg:golang/github.com/aquasecurity/go-direct2@v4.0.0 + // - pkg:golang/github.com/aquasecurity/go-transitive@v5.0.0 goModule := clonePackage(goModulePackage) goDirect1 := clonePackage(goDirectPackage1) goDirect2 := clonePackage(goDirectPackage2) diff --git a/rpc/cache/service.pb.go b/rpc/cache/service.pb.go index d3a1fa1a63b9..acca9623e93a 100644 --- a/rpc/cache/service.pb.go +++ b/rpc/cache/service.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.34.0 // protoc v3.19.4 // source: rpc/cache/service.proto diff --git a/rpc/common/service.pb.go b/rpc/common/service.pb.go index 4c7f0af79464..c8290cc52818 100644 --- a/rpc/common/service.pb.go +++ b/rpc/common/service.pb.go @@ -755,12 +755,11 @@ type Misconfiguration struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FileType string `protobuf:"bytes,1,opt,name=file_type,json=fileType,proto3" json:"file_type,omitempty"` - FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` - Successes []*MisconfResult `protobuf:"bytes,3,rep,name=successes,proto3" json:"successes,omitempty"` - Warnings []*MisconfResult `protobuf:"bytes,4,rep,name=warnings,proto3" json:"warnings,omitempty"` - Failures []*MisconfResult `protobuf:"bytes,5,rep,name=failures,proto3" json:"failures,omitempty"` - Exceptions []*MisconfResult `protobuf:"bytes,6,rep,name=exceptions,proto3" json:"exceptions,omitempty"` + FileType string `protobuf:"bytes,1,opt,name=file_type,json=fileType,proto3" json:"file_type,omitempty"` + FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` + Successes []*MisconfResult `protobuf:"bytes,3,rep,name=successes,proto3" json:"successes,omitempty"` + Warnings []*MisconfResult `protobuf:"bytes,4,rep,name=warnings,proto3" json:"warnings,omitempty"` + Failures []*MisconfResult `protobuf:"bytes,5,rep,name=failures,proto3" json:"failures,omitempty"` } func (x *Misconfiguration) Reset() { @@ -830,13 +829,6 @@ func (x *Misconfiguration) GetFailures() []*MisconfResult { return nil } -func (x *Misconfiguration) GetExceptions() []*MisconfResult { - if x != nil { - return x.Exceptions - } - return nil -} - type MisconfResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2482,7 +2474,7 @@ var file_rpc_common_service_proto_rawDesc = []byte{ 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, - 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0xb6, 0x02, 0x0a, 0x10, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, + 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0xff, 0x01, 0x0a, 0x10, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, @@ -2498,300 +2490,296 @@ var file_rpc_common_service_proto_rawDesc = []byte{ 0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x73, 0x12, 0x3b, 0x0a, 0x0a, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x0a, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xf3, - 0x01, 0x0a, 0x0d, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x45, 0x0a, 0x0f, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, - 0x0e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x07, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x52, - 0x02, 0x69, 0x64, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x22, 0xf0, 0x01, 0x0a, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x64, 0x76, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x76, 0x49, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, - 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, - 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xf7, 0x03, 0x0a, 0x18, 0x44, 0x65, 0x74, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x73, 0x6f, - 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, - 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, - 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, - 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1f, 0x0a, 0x0b, - 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, - 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x12, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x76, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x76, 0x64, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x22, 0xff, 0x09, 0x0a, 0x0d, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, - 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x19, - 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, - 0x69, 0x78, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, + 0x73, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0xf3, 0x01, 0x0a, 0x0d, 0x4d, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x45, 0x0a, 0x0f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, 0x73, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x63, + 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x03, + 0x10, 0x07, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x52, 0x02, 0x69, 0x64, 0x52, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x22, 0xf0, 0x01, + 0x0a, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x15, 0x0a, 0x06, 0x61, 0x64, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x64, 0x76, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, - 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x6b, 0x67, 0x5f, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, - 0x6b, 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0d, 0x70, 0x6b, - 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x05, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, - 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, - 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, - 0x74, 0x79, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x39, 0x0a, 0x04, 0x63, 0x76, 0x73, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, - 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, - 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x43, 0x76, 0x73, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x63, 0x76, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x77, - 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x63, 0x77, 0x65, - 0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x75, - 0x72, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x55, 0x72, 0x6c, 0x12, 0x41, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, - 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, + 0x2f, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x22, 0xf7, 0x03, 0x0a, 0x18, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4d, 0x69, 0x73, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, + 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, + 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, + 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, + 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, + 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, + 0x06, 0x61, 0x76, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, + 0x76, 0x64, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0xff, 0x09, 0x0a, 0x0d, 0x56, + 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, + 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x69, 0x78, 0x65, 0x64, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x08, + 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, + 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, + 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, + 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x6b, 0x67, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x6b, 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0d, 0x70, 0x6b, 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, + 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, + 0x74, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x04, 0x63, 0x76, 0x73, 0x73, + 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x2e, 0x43, 0x76, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x63, + 0x76, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x77, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x63, 0x77, 0x65, 0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x41, 0x0a, + 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, - 0x68, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x10, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x44, 0x61, 0x74, - 0x65, 0x12, 0x48, 0x0a, 0x14, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x76, 0x69, - 0x73, 0x6f, 0x72, 0x79, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x41, - 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x10, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x76, 0x75, 0x6c, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x75, 0x6c, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, - 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x09, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0b, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0a, 0x64, 0x61, 0x74, - 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, - 0x72, 0x5f, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x56, 0x65, - 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0e, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, - 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x16, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x15, 0x0a, 0x06, - 0x70, 0x6b, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6b, - 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x18, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x4b, 0x0a, 0x09, 0x43, - 0x76, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x56, 0x53, 0x53, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x59, 0x0a, 0x13, 0x56, 0x65, 0x6e, 0x64, - 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x42, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x57, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, - 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x69, 0x66, 0x66, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x66, 0x66, 0x49, - 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, - 0x22, 0xc3, 0x01, 0x0a, 0x0d, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, - 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, - 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, - 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, - 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x76, 0x0a, 0x04, 0x43, 0x56, 0x53, 0x53, 0x12, 0x1b, - 0x0a, 0x09, 0x76, 0x32, 0x5f, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x76, 0x32, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x76, - 0x33, 0x5f, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x76, 0x33, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x32, 0x5f, 0x73, - 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x32, 0x53, 0x63, - 0x6f, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x33, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x33, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x98, - 0x01, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x2a, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf3, 0x01, 0x0a, 0x04, 0x4c, 0x69, - 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, - 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, - 0x0b, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x69, 0x72, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x22, - 0x30, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, - 0x73, 0x22, 0x9f, 0x02, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, - 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, - 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x48, 0x0a, 0x14, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x12, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x41, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x76, + 0x75, 0x6c, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x75, + 0x6c, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x76, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x58, 0x0a, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x73, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x69, 0x76, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, + 0x65, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x76, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, + 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, + 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x15, 0x0a, 0x06, 0x70, 0x6b, 0x67, 0x5f, 0x69, 0x64, 0x18, + 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6b, 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x1a, 0x4b, 0x0a, 0x09, 0x43, 0x76, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, - 0x63, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x04, 0x08, - 0x09, 0x10, 0x0a, 0x22, 0x5d, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x69, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, - 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x73, 0x22, 0x99, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4c, - 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, - 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, - 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, - 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, + 0x6e, 0x2e, 0x43, 0x56, 0x53, 0x53, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x59, 0x0a, 0x13, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, + 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x42, 0x0a, 0x0a, + 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x22, 0x57, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x66, 0x66, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x22, 0xc3, 0x01, 0x0a, 0x0d, 0x43, 0x61, + 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, + 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, + 0x76, 0x0a, 0x04, 0x43, 0x56, 0x53, 0x53, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x32, 0x5f, 0x76, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x32, 0x56, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x33, 0x5f, 0x76, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x33, 0x56, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x32, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x32, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x76, 0x33, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, + 0x76, 0x33, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, + 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0xf3, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x69, 0x73, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x69, 0x73, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x75, 0x6e, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x72, 0x75, + 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, + 0x67, 0x68, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x69, 0x67, + 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, + 0x61, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x22, 0x30, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, + 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x9f, 0x02, 0x0a, 0x0d, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, + 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, + 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, 0x0a, + 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, + 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, + 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, + 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0x5d, 0x0a, 0x06, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, + 0x74, 0x68, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x99, 0x02, 0x0a, 0x0f, + 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, + 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, + 0x67, 0x6f, 0x72, 0x79, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, + 0x69, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0xed, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, + 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, + 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, + 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, + 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x0b, 0x6c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, + 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x05, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, + 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, + 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x98, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x63, 0x65, + 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x2e, 0x45, 0x6e, 0x75, 0x6d, - 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, - 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, - 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, - 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0xed, - 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x41, - 0x0a, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, - 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, - 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x69, 0x6e, - 0x67, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, - 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, - 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x67, 0x69, - 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x98, - 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x79, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, - 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, - 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x22, 0x95, 0x01, 0x0a, 0x0f, 0x4c, 0x69, - 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x81, 0x01, - 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x42, 0x49, - 0x44, 0x44, 0x45, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x53, 0x54, 0x52, 0x49, - 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x43, 0x49, 0x50, 0x52, - 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x54, 0x49, 0x43, 0x45, - 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x56, 0x45, - 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x4e, 0x45, 0x4e, 0x43, 0x55, 0x4d, 0x42, 0x45, 0x52, - 0x45, 0x44, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x07, 0x22, 0x4e, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x3f, 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x50, 0x4b, - 0x47, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, - 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, - 0x03, 0x2a, 0x44, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0b, 0x0a, - 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, - 0x57, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x44, 0x49, 0x55, 0x4d, 0x10, 0x02, 0x12, - 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, - 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x04, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x2f, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, + 0x6e, 0x6b, 0x22, 0x95, 0x01, 0x0a, 0x0f, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x81, 0x01, 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, + 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x42, 0x49, 0x44, 0x44, 0x45, 0x4e, 0x10, 0x01, 0x12, + 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, + 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x43, 0x49, 0x50, 0x52, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x03, 0x12, + 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x54, 0x49, 0x43, 0x45, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x50, + 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x56, 0x45, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x55, + 0x4e, 0x45, 0x4e, 0x43, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x45, 0x44, 0x10, 0x06, 0x12, 0x0b, 0x0a, + 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x07, 0x22, 0x4e, 0x0a, 0x0b, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, 0x0a, 0x04, 0x45, 0x6e, 0x75, + 0x6d, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x50, 0x4b, 0x47, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, + 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x43, 0x45, + 0x4e, 0x53, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x44, 0x0a, 0x08, 0x53, 0x65, + 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, + 0x4d, 0x45, 0x44, 0x49, 0x55, 0x4d, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, + 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x04, + 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, + 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x69, 0x76, + 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3b, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2852,42 +2840,41 @@ var file_rpc_common_service_proto_depIdxs = []int32{ 11, // 5: trivy.common.Misconfiguration.successes:type_name -> trivy.common.MisconfResult 11, // 6: trivy.common.Misconfiguration.warnings:type_name -> trivy.common.MisconfResult 11, // 7: trivy.common.Misconfiguration.failures:type_name -> trivy.common.MisconfResult - 11, // 8: trivy.common.Misconfiguration.exceptions:type_name -> trivy.common.MisconfResult - 12, // 9: trivy.common.MisconfResult.policy_metadata:type_name -> trivy.common.PolicyMetadata - 17, // 10: trivy.common.MisconfResult.cause_metadata:type_name -> trivy.common.CauseMetadata - 0, // 11: trivy.common.DetectedMisconfiguration.severity:type_name -> trivy.common.Severity - 16, // 12: trivy.common.DetectedMisconfiguration.layer:type_name -> trivy.common.Layer - 17, // 13: trivy.common.DetectedMisconfiguration.cause_metadata:type_name -> trivy.common.CauseMetadata - 0, // 14: trivy.common.Vulnerability.severity:type_name -> trivy.common.Severity - 8, // 15: trivy.common.Vulnerability.pkg_identifier:type_name -> trivy.common.PkgIdentifier - 16, // 16: trivy.common.Vulnerability.layer:type_name -> trivy.common.Layer - 29, // 17: trivy.common.Vulnerability.cvss:type_name -> trivy.common.Vulnerability.CvssEntry - 31, // 18: trivy.common.Vulnerability.published_date:type_name -> google.protobuf.Timestamp - 31, // 19: trivy.common.Vulnerability.last_modified_date:type_name -> google.protobuf.Timestamp - 32, // 20: trivy.common.Vulnerability.custom_advisory_data:type_name -> google.protobuf.Value - 32, // 21: trivy.common.Vulnerability.custom_vuln_data:type_name -> google.protobuf.Value - 15, // 22: trivy.common.Vulnerability.data_source:type_name -> trivy.common.DataSource - 30, // 23: trivy.common.Vulnerability.vendor_severity:type_name -> trivy.common.Vulnerability.VendorSeverityEntry - 21, // 24: trivy.common.CauseMetadata.code:type_name -> trivy.common.Code - 16, // 25: trivy.common.CustomResource.layer:type_name -> trivy.common.Layer - 32, // 26: trivy.common.CustomResource.data:type_name -> google.protobuf.Value - 20, // 27: trivy.common.Code.lines:type_name -> trivy.common.Line - 21, // 28: trivy.common.SecretFinding.code:type_name -> trivy.common.Code - 16, // 29: trivy.common.SecretFinding.layer:type_name -> trivy.common.Layer - 22, // 30: trivy.common.Secret.findings:type_name -> trivy.common.SecretFinding - 0, // 31: trivy.common.DetectedLicense.severity:type_name -> trivy.common.Severity - 1, // 32: trivy.common.DetectedLicense.category:type_name -> trivy.common.LicenseCategory.Enum - 2, // 33: trivy.common.LicenseFile.license_type:type_name -> trivy.common.LicenseType.Enum - 26, // 34: trivy.common.LicenseFile.fingings:type_name -> trivy.common.LicenseFinding - 16, // 35: trivy.common.LicenseFile.layer:type_name -> trivy.common.Layer - 1, // 36: trivy.common.LicenseFinding.category:type_name -> trivy.common.LicenseCategory.Enum - 18, // 37: trivy.common.Vulnerability.CvssEntry.value:type_name -> trivy.common.CVSS - 0, // 38: trivy.common.Vulnerability.VendorSeverityEntry.value:type_name -> trivy.common.Severity - 39, // [39:39] is the sub-list for method output_type - 39, // [39:39] is the sub-list for method input_type - 39, // [39:39] is the sub-list for extension type_name - 39, // [39:39] is the sub-list for extension extendee - 0, // [0:39] is the sub-list for field type_name + 12, // 8: trivy.common.MisconfResult.policy_metadata:type_name -> trivy.common.PolicyMetadata + 17, // 9: trivy.common.MisconfResult.cause_metadata:type_name -> trivy.common.CauseMetadata + 0, // 10: trivy.common.DetectedMisconfiguration.severity:type_name -> trivy.common.Severity + 16, // 11: trivy.common.DetectedMisconfiguration.layer:type_name -> trivy.common.Layer + 17, // 12: trivy.common.DetectedMisconfiguration.cause_metadata:type_name -> trivy.common.CauseMetadata + 0, // 13: trivy.common.Vulnerability.severity:type_name -> trivy.common.Severity + 8, // 14: trivy.common.Vulnerability.pkg_identifier:type_name -> trivy.common.PkgIdentifier + 16, // 15: trivy.common.Vulnerability.layer:type_name -> trivy.common.Layer + 29, // 16: trivy.common.Vulnerability.cvss:type_name -> trivy.common.Vulnerability.CvssEntry + 31, // 17: trivy.common.Vulnerability.published_date:type_name -> google.protobuf.Timestamp + 31, // 18: trivy.common.Vulnerability.last_modified_date:type_name -> google.protobuf.Timestamp + 32, // 19: trivy.common.Vulnerability.custom_advisory_data:type_name -> google.protobuf.Value + 32, // 20: trivy.common.Vulnerability.custom_vuln_data:type_name -> google.protobuf.Value + 15, // 21: trivy.common.Vulnerability.data_source:type_name -> trivy.common.DataSource + 30, // 22: trivy.common.Vulnerability.vendor_severity:type_name -> trivy.common.Vulnerability.VendorSeverityEntry + 21, // 23: trivy.common.CauseMetadata.code:type_name -> trivy.common.Code + 16, // 24: trivy.common.CustomResource.layer:type_name -> trivy.common.Layer + 32, // 25: trivy.common.CustomResource.data:type_name -> google.protobuf.Value + 20, // 26: trivy.common.Code.lines:type_name -> trivy.common.Line + 21, // 27: trivy.common.SecretFinding.code:type_name -> trivy.common.Code + 16, // 28: trivy.common.SecretFinding.layer:type_name -> trivy.common.Layer + 22, // 29: trivy.common.Secret.findings:type_name -> trivy.common.SecretFinding + 0, // 30: trivy.common.DetectedLicense.severity:type_name -> trivy.common.Severity + 1, // 31: trivy.common.DetectedLicense.category:type_name -> trivy.common.LicenseCategory.Enum + 2, // 32: trivy.common.LicenseFile.license_type:type_name -> trivy.common.LicenseType.Enum + 26, // 33: trivy.common.LicenseFile.fingings:type_name -> trivy.common.LicenseFinding + 16, // 34: trivy.common.LicenseFile.layer:type_name -> trivy.common.Layer + 1, // 35: trivy.common.LicenseFinding.category:type_name -> trivy.common.LicenseCategory.Enum + 18, // 36: trivy.common.Vulnerability.CvssEntry.value:type_name -> trivy.common.CVSS + 0, // 37: trivy.common.Vulnerability.VendorSeverityEntry.value:type_name -> trivy.common.Severity + 38, // [38:38] is the sub-list for method output_type + 38, // [38:38] is the sub-list for method input_type + 38, // [38:38] is the sub-list for extension type_name + 38, // [38:38] is the sub-list for extension extendee + 0, // [0:38] is the sub-list for field type_name } func init() { file_rpc_common_service_proto_init() } diff --git a/rpc/common/service.proto b/rpc/common/service.proto index d80bf57e3b86..e989738c285b 100644 --- a/rpc/common/service.proto +++ b/rpc/common/service.proto @@ -73,7 +73,8 @@ message Misconfiguration { repeated MisconfResult successes = 3; repeated MisconfResult warnings = 4; repeated MisconfResult failures = 5; - repeated MisconfResult exceptions = 6; + + reserved 6; // deprecated 'exceptions' } message MisconfResult {