mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-21 18:58:04 +00:00
173 lines
6.3 KiB
YAML
173 lines
6.3 KiB
YAML
name: 'Container Security Scan with Trivy'
|
|
description: 'Scans container images for vulnerabilities using Trivy and reports results'
|
|
author: 'Prowler'
|
|
|
|
inputs:
|
|
image-name:
|
|
description: 'Container image name to scan'
|
|
required: true
|
|
image-tag:
|
|
description: 'Container image tag to scan'
|
|
required: true
|
|
default: ${{ github.sha }}
|
|
severity:
|
|
description: 'Severities to scan for (comma-separated)'
|
|
required: false
|
|
default: 'CRITICAL,HIGH,MEDIUM,LOW'
|
|
fail-on-critical:
|
|
description: 'Fail the build if critical vulnerabilities are found'
|
|
required: false
|
|
default: 'false'
|
|
upload-sarif:
|
|
description: 'Upload results to GitHub Security tab'
|
|
required: false
|
|
default: 'true'
|
|
create-pr-comment:
|
|
description: 'Create a comment on the PR with scan results'
|
|
required: false
|
|
default: 'true'
|
|
artifact-retention-days:
|
|
description: 'Days to retain the Trivy report artifact'
|
|
required: false
|
|
default: '2'
|
|
|
|
outputs:
|
|
critical-count:
|
|
description: 'Number of critical vulnerabilities found'
|
|
value: ${{ steps.security-check.outputs.critical }}
|
|
high-count:
|
|
description: 'Number of high vulnerabilities found'
|
|
value: ${{ steps.security-check.outputs.high }}
|
|
total-count:
|
|
description: 'Total number of vulnerabilities found'
|
|
value: ${{ steps.security-check.outputs.total }}
|
|
|
|
runs:
|
|
using: 'composite'
|
|
steps:
|
|
- name: Cache Trivy vulnerability database
|
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
|
with:
|
|
path: ~/.cache/trivy
|
|
key: trivy-db-${{ runner.os }}-${{ github.run_id }}
|
|
restore-keys: |
|
|
trivy-db-${{ runner.os }}-
|
|
|
|
- name: Run Trivy vulnerability scan (JSON)
|
|
uses: aquasecurity/trivy-action@e368e328979b113139d6f9068e03accaed98a518 # 0.34.1
|
|
with:
|
|
image-ref: ${{ inputs.image-name }}:${{ inputs.image-tag }}
|
|
format: 'json'
|
|
output: 'trivy-report.json'
|
|
severity: ${{ inputs.severity }}
|
|
exit-code: '0'
|
|
scanners: 'vuln'
|
|
timeout: '5m'
|
|
version: 'v0.69.2'
|
|
|
|
- name: Run Trivy vulnerability scan (SARIF)
|
|
if: inputs.upload-sarif == 'true' && github.event_name == 'push'
|
|
uses: aquasecurity/trivy-action@e368e328979b113139d6f9068e03accaed98a518 # 0.34.1
|
|
with:
|
|
image-ref: ${{ inputs.image-name }}:${{ inputs.image-tag }}
|
|
format: 'sarif'
|
|
output: 'trivy-results.sarif'
|
|
severity: 'CRITICAL,HIGH'
|
|
exit-code: '0'
|
|
scanners: 'vuln'
|
|
timeout: '5m'
|
|
version: 'v0.69.2'
|
|
|
|
- name: Upload Trivy results to GitHub Security tab
|
|
if: inputs.upload-sarif == 'true' && github.event_name == 'push'
|
|
uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
|
with:
|
|
sarif_file: 'trivy-results.sarif'
|
|
category: 'trivy-container'
|
|
|
|
- name: Upload Trivy report artifact
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
if: always()
|
|
with:
|
|
name: trivy-scan-report-${{ inputs.image-name }}-${{ inputs.image-tag }}
|
|
path: trivy-report.json
|
|
retention-days: ${{ inputs.artifact-retention-days }}
|
|
|
|
- name: Generate security summary
|
|
id: security-check
|
|
shell: bash
|
|
run: |
|
|
CRITICAL=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="CRITICAL")] | length' trivy-report.json)
|
|
HIGH=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="HIGH")] | length' trivy-report.json)
|
|
TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-report.json)
|
|
|
|
echo "critical=$CRITICAL" >> $GITHUB_OUTPUT
|
|
echo "high=$HIGH" >> $GITHUB_OUTPUT
|
|
echo "total=$TOTAL" >> $GITHUB_OUTPUT
|
|
|
|
echo "### 🔒 Container Security Scan" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Image:** \`${INPUTS_IMAGE_NAME}:${INPUTS_IMAGE_TAG}\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "- 🔴 Critical: $CRITICAL" >> $GITHUB_STEP_SUMMARY
|
|
echo "- 🟠 High: $HIGH" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Total**: $TOTAL" >> $GITHUB_STEP_SUMMARY
|
|
env:
|
|
INPUTS_IMAGE_NAME: ${{ inputs.image-name }}
|
|
INPUTS_IMAGE_TAG: ${{ inputs.image-tag }}
|
|
|
|
- name: Comment scan results on PR
|
|
if: inputs.create-pr-comment == 'true' && github.event_name == 'pull_request'
|
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
|
env:
|
|
IMAGE_NAME: ${{ inputs.image-name }}
|
|
GITHUB_SHA: ${{ inputs.image-tag }}
|
|
SEVERITY: ${{ inputs.severity }}
|
|
with:
|
|
script: |
|
|
const comment = require('./.github/scripts/trivy-pr-comment.js');
|
|
|
|
// Unique identifier to find our comment
|
|
const marker = `<!-- trivy-scan-comment:${process.env.IMAGE_NAME} -->`;
|
|
const body = marker + '\n' + comment;
|
|
|
|
// Find existing comment
|
|
const { data: comments } = await github.rest.issues.listComments({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: context.issue.number,
|
|
});
|
|
|
|
const existingComment = comments.find(c => c.body?.includes(marker));
|
|
|
|
if (existingComment) {
|
|
// Update existing comment
|
|
await github.rest.issues.updateComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
comment_id: existingComment.id,
|
|
body: body
|
|
});
|
|
console.log('✅ Updated existing Trivy scan comment');
|
|
} else {
|
|
// Create new comment
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: context.issue.number,
|
|
body: body
|
|
});
|
|
console.log('✅ Created new Trivy scan comment');
|
|
}
|
|
|
|
- name: Check for critical vulnerabilities
|
|
if: inputs.fail-on-critical == 'true' && steps.security-check.outputs.critical != '0'
|
|
shell: bash
|
|
run: |
|
|
echo "::error::Found ${STEPS_SECURITY_CHECK_OUTPUTS_CRITICAL} critical vulnerabilities"
|
|
echo "::warning::Please update packages or use a different base image"
|
|
exit 1
|
|
|
|
env:
|
|
STEPS_SECURITY_CHECK_OUTPUTS_CRITICAL: ${{ steps.security-check.outputs.critical }}
|