From 1534331d1713f1112025098539202773aaaff56a Mon Sep 17 00:00:00 2001 From: Chris Bandy Date: Fri, 15 Nov 2024 23:22:48 -0600 Subject: [PATCH 1/2] Stop using the upstream Trivy action It lacks sufficient settings to control its cache and setup steps. Download Trivy data in a separate job and limit its concurrency. Issue: PGO-1893 --- .github/actions/trivy/action.yaml | 99 +++++++++++++++++++++++++++++++ .github/workflows/trivy.yaml | 83 ++++++++------------------ 2 files changed, 125 insertions(+), 57 deletions(-) create mode 100644 .github/actions/trivy/action.yaml diff --git a/.github/actions/trivy/action.yaml b/.github/actions/trivy/action.yaml new file mode 100644 index 0000000000..6b511c3bba --- /dev/null +++ b/.github/actions/trivy/action.yaml @@ -0,0 +1,99 @@ +name: Trivy +description: Scan this project using Trivy + +# The Trivy team maintains an action, but it has trouble caching its vulnerability data: +# https://github.com/aquasecurity/trivy-action/issues/389 +# +# The action below uses any recent cache matching `cache-prefix` and calculates a cache key +# derived from the data Trivy downloads. + +inputs: + cache: + default: restore,success,use + description: >- + What Trivy data to cache; one or more of restore, save, success, or use. + + setup: + default: v0.57.0,cache + description: >- + How to install Trivy; one or more of version, none, or cache. + + cache-directory: + default: ${{ github.workspace }}/.cache/trivy + + cache-prefix: + default: cache-trivy + + scan-target: + default: . + + scan-type: + default: filesystem + +runs: + using: composite + steps: + # Parse list inputs as separated by commas and spaces. + # Select the maximum version-looking string from `inputs.setup`. + - id: parsed + shell: bash + run: | + # Validate inputs + ( + <<< '${{ inputs.cache }}' jq -rRsS '"cache=\(split("[,\\s]+"; "") - [""])"' + <<< '${{ inputs.setup }}' jq -rRsS ' + "setup=\(split("[,\\s]+"; "") - [""])", + "version=\(split("[,\\s]+"; "") | max_by(split("[v.]"; "") | map(tonumber?)))" + ' + ) | tee --append $GITHUB_OUTPUT + + # Install Trivy as requested. + - if: ${{ ! contains(fromJSON(steps.parsed.outputs.setup), 'none') }} + uses: aquasecurity/setup-trivy@v0.2.2 + with: + cache: ${{ contains(fromJSON(steps.parsed.outputs.setup), 'cache') }} + version: ${{ steps.parsed.outputs.version }} + + # Restore a recent cache beginning with the prefix. + - id: restore + if: ${{ contains(fromJSON(steps.parsed.outputs.cache), 'restore') }} + uses: actions/cache/restore@v4 + with: + path: ${{ inputs.cache-directory }} + key: ${{ inputs.cache-prefix }}- + + - id: trivy + shell: bash + env: + TRIVY_CACHE_DIR: >- + ${{ contains(fromJSON(steps.parsed.outputs.cache), 'use') && inputs.cache-directory || '' }} + run: | + # Run Trivy + trivy '${{ inputs.scan-type }}' '${{ inputs.scan-target }}' || result=$? + + checksum=$([[ -z "${TRIVY_CACHE_DIR}" ]] || cat "${TRIVY_CACHE_DIR}/"*/metadata.json | sha256sum) + echo 'cache-key=${{ inputs.cache-prefix }}-'"${checksum%% *}" >> $GITHUB_OUTPUT + + exit "${result-0}" + + # Save updated data to the cache when requested. + - if: >- + ${{ + steps.restore.outcome == 'success' && + steps.restore.outputs.cache-matched-key == steps.trivy.outputs.cache-key + }} + shell: bash + run: | + # Cache hit on ${{ steps.restore.outputs.cache-matched-key }} + - if: >- + ${{ + steps.restore.outputs.cache-matched-key != steps.trivy.outputs.cache-key && + ( + (contains(fromJSON(steps.parsed.outputs.cache), 'save') && !cancelled()) || + (contains(fromJSON(steps.parsed.outputs.cache), 'success') && success()) + ) + }} + uses: actions/cache/save@v4 + with: + key: ${{ steps.trivy.outputs.cache-key }} + path: ${{ inputs.cache-directory }} diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index f6f4b2ca2d..a8447ee870 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -12,55 +12,21 @@ env: # https://github.com/actions/setup-go/issues/457 GOTOOLCHAIN: local - # Manage the Trivy data directory until upstream can do it reliably - # https://github.com/aquasecurity/trivy-action/issues/389 - # - # NOTE: This must match the default "cache-dir" upstream: - # https://github.com/aquasecurity/trivy-action/blob/-/action.yaml - TRIVY_CACHE_DIR: ${{ github.workspace }}/.cache/trivy - jobs: cache: + # Run only one of these jobs at a time across the entire project. + concurrency: { group: trivy-cache } + runs-on: ubuntu-latest steps: - - uses: aquasecurity/setup-trivy@v0.2.2 - with: - cache: true - version: v0.57.0 - - # The "aquasecurity/trivy-action" looks for data in the GitHub action - # cache under a key with today's date. - # - https://github.com/actions/cache/blob/-/restore#readme - # - https://github.com/aquasecurity/trivy-action/blob/-/action.yaml - - id: values - run: | - ( - date +'date=%Y-%m-%d' - echo "glob=${TRIVY_CACHE_DIR}/*/metadata.json" - ) | - tee --append $GITHUB_OUTPUT - - id: restore - uses: actions/cache/restore@v4 - with: - key: cache-trivy-${{ steps.values.outputs.date }} - path: ${{ env.TRIVY_CACHE_DIR }} - restore-keys: cache-trivy- - - # Validate or update the Trivy data cache. - - id: validate + - uses: actions/checkout@v4 + - name: Download Trivy + uses: ./.github/actions/trivy env: - METADATA_HASH: ${{ hashFiles(steps.values.outputs.glob) }} - run: | - <<< "before=${METADATA_HASH}" tee --append $GITHUB_OUTPUT - trivy filesystem --download-db-only --scanners license,secret,vuln --quiet - - # Save any successful changes back to the GitHub action cache. - # - https://github.com/actions/cache/blob/-/save#readme - - if: ${{ hashFiles(steps.values.outputs.glob) != steps.validate.outputs.before }} - uses: actions/cache/save@v4 - with: - key: ${{ steps.restore.outputs.cache-primary-key }} - path: ${{ env.TRIVY_CACHE_DIR }} + TRIVY_DEBUG: true + TRIVY_DOWNLOAD_DB_ONLY: true + TRIVY_NO_PROGRESS: true + TRIVY_SCANNERS: license,secret,vuln licenses: needs: [cache] @@ -75,13 +41,13 @@ jobs: # Report success only when detected licenses are listed in [/trivy.yaml]. - name: Scan licenses - uses: aquasecurity/trivy-action@0.28.0 + uses: ./.github/actions/trivy env: TRIVY_DEBUG: true + TRIVY_EXIT_CODE: 1 + TRIVY_SCANNERS: license with: - scan-type: filesystem - scanners: license - exit-code: 1 + cache: restore,use vulnerabilities: if: ${{ github.repository == 'CrunchyData/postgres-operator' }} @@ -97,21 +63,24 @@ jobs: # human consumption. This step fails only when Trivy is unable to scan. # A later step uploads results to GitHub as a pull request check. - name: Log detected vulnerabilities - uses: aquasecurity/trivy-action@0.28.0 + uses: ./.github/actions/trivy + env: + TRIVY_SCANNERS: secret,vuln with: - scan-type: filesystem - scanners: secret,vuln + cache: restore,use # Produce a SARIF report of actionable results. This step fails only when # Trivy is unable to scan. - name: Report actionable vulnerabilities - uses: aquasecurity/trivy-action@0.28.0 + uses: ./.github/actions/trivy + env: + TRIVY_IGNORE_UNFIXED: true + TRIVY_FORMAT: 'sarif' + TRIVY_OUTPUT: 'trivy-results.sarif' + TRIVY_SCANNERS: secret,vuln with: - scan-type: filesystem - ignore-unfixed: true - format: 'sarif' - output: 'trivy-results.sarif' - scanners: secret,vuln + cache: use + setup: none # Submit the SARIF report to GitHub code scanning. Pull requests checks # succeed or fail according to branch protection rules. From bd4a91e5324337b2dcae09f298aeb490bdad25a1 Mon Sep 17 00:00:00 2001 From: Chris Bandy Date: Sat, 16 Nov 2024 22:37:39 -0600 Subject: [PATCH 2/2] Continue the Trivy workflow when its data download fails Later steps will use data from the action cache and ignore its age. The workflow fails when the download fails and the cache is empty. Issue: PGO-1893 --- .github/actions/trivy/action.yaml | 8 ++++++++ .github/workflows/trivy.yaml | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/actions/trivy/action.yaml b/.github/actions/trivy/action.yaml index 6b511c3bba..b692062480 100644 --- a/.github/actions/trivy/action.yaml +++ b/.github/actions/trivy/action.yaml @@ -13,6 +13,11 @@ inputs: description: >- What Trivy data to cache; one or more of restore, save, success, or use. + database: + default: update + description: >- + How Trivy should handle its data; one of update or skip. + setup: default: v0.57.0,cache description: >- @@ -67,6 +72,9 @@ runs: env: TRIVY_CACHE_DIR: >- ${{ contains(fromJSON(steps.parsed.outputs.cache), 'use') && inputs.cache-directory || '' }} + TRIVY_SKIP_CHECK_UPDATE: ${{ inputs.database == 'skip' }} + TRIVY_SKIP_DB_UPDATE: ${{ inputs.database == 'skip' }} + TRIVY_SKIP_JAVA_DB_UPDATE: ${{ inputs.database == 'skip' }} run: | # Run Trivy trivy '${{ inputs.scan-type }}' '${{ inputs.scan-target }}' || result=$? diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml index a8447ee870..d338563b48 100644 --- a/.github/workflows/trivy.yaml +++ b/.github/workflows/trivy.yaml @@ -16,6 +16,8 @@ jobs: cache: # Run only one of these jobs at a time across the entire project. concurrency: { group: trivy-cache } + # Do not fail this workflow when this job fails. + continue-on-error: true runs-on: ubuntu-latest steps: @@ -29,7 +31,11 @@ jobs: TRIVY_SCANNERS: license,secret,vuln licenses: + # Run this job after the cache job regardless of its success or failure. needs: [cache] + if: >- + ${{ !cancelled() }} + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -48,13 +54,16 @@ jobs: TRIVY_SCANNERS: license with: cache: restore,use + database: skip vulnerabilities: - if: ${{ github.repository == 'CrunchyData/postgres-operator' }} + # Run this job after the cache job regardless of its success or failure. + needs: [cache] + if: >- + ${{ github.repository == 'CrunchyData/postgres-operator' && !cancelled() }} permissions: security-events: write - needs: [cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -68,6 +77,7 @@ jobs: TRIVY_SCANNERS: secret,vuln with: cache: restore,use + database: skip # Produce a SARIF report of actionable results. This step fails only when # Trivy is unable to scan. @@ -80,6 +90,7 @@ jobs: TRIVY_SCANNERS: secret,vuln with: cache: use + database: skip setup: none # Submit the SARIF report to GitHub code scanning. Pull requests checks