-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
1 changed file
with
64 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,30 +46,35 @@ runs: | |
trivy-config: .trivy/${{ steps.configuration_file.outputs.config_file_type }}-config.yaml | ||
continue-on-error: true | ||
|
||
- name: Set retention days | ||
id: set_retention_days | ||
- name: Set artifact upload metadata | ||
id: artifact_metadata | ||
run: | | ||
if [[ '${{ github.ref }}' == 'refs/heads/main' ]]; then | ||
if [[ '${{ github.ref_name }}' == 'main' ]]; then | ||
echo "Keep trivy artifact for 90 days on main branch merge" | ||
echo "days=90" >> "$GITHUB_OUTPUT" | ||
else | ||
echo "Keep trivy artifact for 1 day on PR builds" | ||
echo "days=1" >> "$GITHUB_OUTPUT" | ||
fi | ||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT | ||
sanitized_ref_name=$(echo "${{ github.ref_name }}" | sed 's/[\\\/:*?<>|]/-/g') | ||
echo "ref_name=${sanitized_ref_name}" >> $GITHUB_OUTPUT | ||
shell: bash | ||
|
||
- name: Upload Trivy report to artifacts | ||
uses: actions/upload-artifact@v4 | ||
id: trivy_artifact_upload | ||
with: | ||
name: trivy-${{ inputs.scan-type }}-results | ||
name: trivy-${{ inputs.scan-type }}-results-${{ steps.artifact_metadata.outputs.sha_short }}-${{ steps.artifact_metadata.outputs.ref_name }} | ||
path: trivy-${{ inputs.scan-type }}-results.json | ||
retention-days: ${{ steps.set_retention_days.outputs.days }} | ||
retention-days: ${{ steps.artifact_metadata.outputs.days }} | ||
if-no-files-found: ignore | ||
overwrite: true | ||
|
||
- name: Upload Trivy Report as PR Comment and parse Critical vulnerabilities | ||
id: trivy_report_notification | ||
if: ${{ steps.trivy_scan.outcome == 'failure' && inputs.github-token }} | ||
if: ${{ inputs.github-token }} | ||
uses: actions/github-script@v7 | ||
env: | ||
GITHUB_TOKEN: ${{ inputs.github-token }} | ||
|
@@ -88,45 +93,69 @@ runs: | |
const fileContent = fs.readFileSync(filePath, 'utf8'); | ||
const jsonData = JSON.parse(fileContent); | ||
function formatVulnerabilities(vulnerabilities) { | ||
const headers = ["Library", "Vulnerability", "Severity", "Status", "Installed Version", "Fixed Version", "Title"]; | ||
function formatVulnerabilities(results) { | ||
const headers = ["Library", "Vulnerability", "Severity", "Status", "Installed Version", "Fixed Version", "Title", "Target"]; | ||
const headerRow = `| ${headers.join(" | ")} |`; | ||
const separatorRow = `| ${headers.map(() => "---").join(" | ")} |`; | ||
const rows = vulnerabilities.map(vuln => { | ||
const titleWithUrl = `${vuln.Title || ""} (${vuln.PrimaryURL || ""})`; | ||
return `| ${[ | ||
vuln.PkgName || "", | ||
vuln.VulnerabilityID || "", | ||
vuln.Severity || "", | ||
vuln.Status || "", | ||
vuln.InstalledVersion || "", | ||
vuln.FixedVersion || "", | ||
titleWithUrl | ||
].join(" | ")} |`; | ||
const rows = results.flatMap(result => { | ||
if (result.Vulnerabilities && result.Vulnerabilities.length > 0) { | ||
return result.Vulnerabilities.map(vuln => { | ||
const titleWithUrl = `${vuln.Title || ""} (${vuln.PrimaryURL || ""})`; | ||
return `| ${[ | ||
vuln.PkgName || "", | ||
vuln.VulnerabilityID || "", | ||
vuln.Severity || "", | ||
vuln.Status || "", | ||
vuln.InstalledVersion || "", | ||
vuln.FixedVersion || "", | ||
titleWithUrl, | ||
result.Target || "" | ||
].join(" | ")} |`; | ||
}); | ||
} else { | ||
return []; | ||
} | ||
}); | ||
return [headerRow, separatorRow, ...rows].join("\n"); | ||
} | ||
function parseTrivyResults(data) { | ||
const allVulnerabilities = []; | ||
data.Results.forEach(result => { | ||
if (result.Vulnerabilities) { | ||
result.Vulnerabilities.forEach(vuln => { | ||
allVulnerabilities.push(vuln); | ||
}); | ||
} | ||
}); | ||
return allVulnerabilities; | ||
return data.Results.map(result => ({ | ||
Target: result.Target, | ||
Vulnerabilities: result.Vulnerabilities || [] | ||
})); | ||
} | ||
const vulnerabilities = parseTrivyResults(jsonData); | ||
const criticalVulnerabilitiesCount = vulnerabilities.filter(vuln => vuln.Severity === 'CRITICAL').length; | ||
const criticalVulnerabilitiesCount = vulnerabilities.flatMap(result => result.Vulnerabilities).filter(vuln => vuln.Severity === 'CRITICAL').length; | ||
core.setOutput('critical_vulnerabilities_count', criticalVulnerabilitiesCount.toString()); | ||
core.info(`Found ${criticalVulnerabilitiesCount} critical vulnerabilities`); | ||
const comments = await github.rest.issues.listComments({ | ||
owner, | ||
repo, | ||
issue_number, | ||
}); | ||
const botComment = comments.data.find(comment => comment.body.includes(commentIdentifier)); | ||
core.info(`Parsed Vulnerabilities: ${JSON.stringify(vulnerabilities)}`) | ||
if (vulnerabilities.flatMap(result => result.Vulnerabilities).length === 0) { | ||
if (botComment) { | ||
const noErrorsComment = `No vulnerabilities to be reported.\n${commentIdentifier}`; | ||
if (vulnerabilities.length === 0) { | ||
console.log("No vulnerabilities found."); | ||
await github.rest.issues.updateComment({ | ||
owner, | ||
repo, | ||
comment_id: botComment.id, | ||
body: noErrorsComment, | ||
}); | ||
core.info('Updated existing PR comment to indicate no vulnerabilities detected anymore'); | ||
} else { | ||
core.info('No vulnerabilities found, no comment posted'); | ||
} | ||
} else { | ||
const formattedContent = ` | ||
<details> | ||
|
@@ -139,13 +168,6 @@ runs: | |
const MAX_COMMENT_LENGTH = 65536; | ||
const comments = await github.rest.issues.listComments({ | ||
owner, | ||
repo, | ||
issue_number, | ||
}); | ||
const botComment = comments.data.find(comment => comment.body.includes(commentIdentifier)); | ||
let fullCommentBody; | ||
if (formattedContent.length > MAX_COMMENT_LENGTH) { | ||
|
@@ -170,19 +192,20 @@ runs: | |
comment_id: botComment.id, | ||
body: fullCommentBody, | ||
}); | ||
core.info('Updated existing comment'); | ||
core.info('Updated existing PR comment'); | ||
} else { | ||
await github.rest.issues.createComment({ | ||
owner, | ||
repo, | ||
issue_number, | ||
body: fullCommentBody, | ||
}); | ||
core.info('Created new Pr comment'); | ||
} | ||
} | ||
- name: Notify Slack of critical vulnerabilities | ||
if: ${{ steps.trivy_report_notification.outputs.critical_vulnerabilities_count != '0' && github.ref == 'refs/heads/main' && inputs.slack-bot-token }} | ||
if: ${{ steps.trivy_report_notification.outputs.critical_vulnerabilities_count != '0' && github.ref_name == 'main' && inputs.slack-bot-token }} | ||
uses: slackapi/[email protected] | ||
env: | ||
SLACK_BOT_TOKEN: ${{ inputs.slack-bot-token }} | ||
|
@@ -207,7 +230,7 @@ runs: | |
}, | ||
{ | ||
"type": "mrkdwn", | ||
"text": "*Branch:*\n`${{ github.ref }}`" | ||
"text": "*Branch:*\n`${{ github.ref_name }}`" | ||
} | ||
] | ||
}, | ||
|