Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run Trivy directly #4035

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/actions/trivy/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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.

database:
default: update
description: >-
How Trivy should handle its data; one of update or skip.

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 || '' }}
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=$?

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 }}
98 changes: 39 additions & 59 deletions .github/workflows/trivy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,58 +12,30 @@ 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 }
# Do not fail this workflow when this job fails.
continue-on-error: true

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:
# 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
Expand All @@ -75,20 +47,23 @@ 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
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
Expand All @@ -97,21 +72,26 @@ 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
database: skip

# 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
database: skip
setup: none

# Submit the SARIF report to GitHub code scanning. Pull requests checks
# succeed or fail according to branch protection rules.
Expand Down