mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-05 03:12:14 +00:00
ci(container): gate Grype only on fixable findings, and comment results on the PR (#12341)
This commit is contained in:
@@ -18,6 +18,10 @@ inputs:
|
||||
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 Grype report artifact'
|
||||
required: false
|
||||
@@ -45,6 +49,7 @@ runs:
|
||||
output-file: 'grype-report.json'
|
||||
fail-build: 'false'
|
||||
by-cve: 'true' # Report CVE ids rather than GHSA, so findings line up with Trivy's
|
||||
only-fixed: 'true' # A finding with no available fix is not actionable, so it must not gate
|
||||
cache-db: 'true'
|
||||
grype-version: 'v0.116.1'
|
||||
|
||||
@@ -58,6 +63,7 @@ runs:
|
||||
fail-build: 'false'
|
||||
severity-cutoff: 'high'
|
||||
by-cve: 'true'
|
||||
only-fixed: 'true' # A finding with no available fix is not actionable, so it must not gate
|
||||
cache-db: 'true'
|
||||
grype-version: 'v0.116.1'
|
||||
|
||||
@@ -101,6 +107,51 @@ runs:
|
||||
INPUTS_IMAGE_NAME: ${{ inputs.image-name }}
|
||||
INPUTS_IMAGE_TAG: ${{ inputs.image-tag }}
|
||||
|
||||
# Before the gate, so the comment is there to explain a failure rather than absent because of it
|
||||
- name: Comment scan results on PR
|
||||
if: >-
|
||||
inputs.create-pr-comment == 'true'
|
||||
&& github.event_name == 'pull_request'
|
||||
&& github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
env:
|
||||
IMAGE_NAME: ${{ inputs.image-name }}
|
||||
GITHUB_SHA: ${{ inputs.image-tag }}
|
||||
CUTOFF: ${{ inputs.fail-on-severity }}
|
||||
with:
|
||||
script: |
|
||||
const comment = require('./.github/scripts/grype-pr-comment.js');
|
||||
|
||||
// Unique identifier to find our comment
|
||||
const marker = `<!-- grype-scan-comment:${process.env.IMAGE_NAME} -->`;
|
||||
const body = marker + '\n' + 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) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existingComment.id,
|
||||
body: body
|
||||
});
|
||||
console.log('✅ Updated existing Grype scan comment');
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: body
|
||||
});
|
||||
console.log('✅ Created new Grype scan comment');
|
||||
}
|
||||
|
||||
- name: Check for blocking vulnerabilities
|
||||
if: inputs.fail-on-severity != 'none'
|
||||
shell: bash
|
||||
|
||||
@@ -14,10 +14,10 @@ inputs:
|
||||
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'
|
||||
fail-on-severity:
|
||||
description: 'Fail the build on findings at this severity or above: critical, high, or none'
|
||||
required: false
|
||||
default: 'false'
|
||||
default: 'high'
|
||||
upload-sarif:
|
||||
description: 'Upload results to GitHub Security tab'
|
||||
required: false
|
||||
@@ -62,6 +62,7 @@ runs:
|
||||
severity: ${{ inputs.severity }}
|
||||
exit-code: '0'
|
||||
scanners: 'vuln'
|
||||
ignore-unfixed: 'true' # A finding with no available fix is not actionable, so it must not gate
|
||||
timeout: '5m'
|
||||
version: 'v0.71.2'
|
||||
# Not trivyignores: that input drops the .yaml extension Trivy parses by.
|
||||
@@ -78,6 +79,7 @@ runs:
|
||||
severity: 'CRITICAL,HIGH'
|
||||
exit-code: '0'
|
||||
scanners: 'vuln'
|
||||
ignore-unfixed: 'true' # A finding with no available fix is not actionable, so it must not gate
|
||||
timeout: '5m'
|
||||
version: 'v0.71.2'
|
||||
# Not trivyignores: that input drops the .yaml extension Trivy parses by.
|
||||
@@ -169,13 +171,28 @@ runs:
|
||||
console.log('✅ Created new Trivy scan comment');
|
||||
}
|
||||
|
||||
- name: Check for critical vulnerabilities
|
||||
if: inputs.fail-on-critical == 'true' && steps.security-check.outputs.critical != '0'
|
||||
- name: Check for blocking vulnerabilities
|
||||
if: inputs.fail-on-severity != 'none'
|
||||
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
|
||||
if [ "$CUTOFF" = "critical" ]; then
|
||||
BLOCKING=$CRITICAL
|
||||
SEVERITIES='["CRITICAL"]'
|
||||
else
|
||||
BLOCKING=$((CRITICAL + HIGH))
|
||||
SEVERITIES='["CRITICAL","HIGH"]'
|
||||
fi
|
||||
|
||||
if [ "$BLOCKING" -gt 0 ]; then
|
||||
echo "::error::Found $BLOCKING vulnerabilities at severity ${CUTOFF} or above ($CRITICAL critical, $HIGH high)"
|
||||
echo "::warning::Update the package, or add it to .trivyignore.yaml with a reason if nothing can be done"
|
||||
jq -r --argjson severities "$SEVERITIES" \
|
||||
'.Results[]?.Vulnerabilities[]? | select(.Severity | IN($severities[]))
|
||||
| " \(.Severity)\t\(.VulnerabilityID)\t\(.PkgName) \(.InstalledVersion)"' \
|
||||
trivy-report.json | sort -u
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
STEPS_SECURITY_CHECK_OUTPUTS_CRITICAL: ${{ steps.security-check.outputs.critical }}
|
||||
CUTOFF: ${{ inputs.fail-on-severity }}
|
||||
CRITICAL: ${{ steps.security-check.outputs.critical }}
|
||||
HIGH: ${{ steps.security-check.outputs.high }}
|
||||
|
||||
Reference in New Issue
Block a user