mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
ci: Cat 1072 security nightly cleanup (#17983)
This commit is contained in:
269
.github/workflows/security-trivy-scan-callable.yml
vendored
269
.github/workflows/security-trivy-scan-callable.yml
vendored
@@ -4,123 +4,206 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
image_ref:
|
image_ref:
|
||||||
description: Full image reference to scan e.g ghcr.io/n8n-io/n8n:latest
|
description: 'Full image reference to scan e.g. ghcr.io/n8n-io/n8n:latest'
|
||||||
required: true
|
required: true
|
||||||
default: 'ghcr.io/n8n-io/n8n:latest'
|
default: 'ghcr.io/n8n-io/n8n:latest'
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
image_ref:
|
image_ref:
|
||||||
type: string
|
type: string
|
||||||
description: Full image reference to scan e.g ghcr.io/n8n-io/n8n:latest
|
description: 'Full image reference to scan e.g. ghcr.io/n8n-io/n8n:latest'
|
||||||
required: true
|
required: true
|
||||||
secrets:
|
secrets:
|
||||||
SLACK_WEBHOOK_URL:
|
SLACK_BOT_TOKEN:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||||
|
SLACK_CHANNEL_ID: C042WDXPTEZ #mission-security
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
security_scan:
|
security_scan:
|
||||||
|
name: Security - Scan Docker Image With Trivy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
||||||
|
|
||||||
- name: Pull Docker image with retry
|
|
||||||
run: |
|
|
||||||
for i in {1..4}; do
|
|
||||||
docker pull "${{ inputs.image_ref }}" && break
|
|
||||||
[ $i -lt 4 ] && echo "Retry $i failed, waiting..." && sleep 15
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Run Trivy vulnerability scanner
|
- name: Run Trivy vulnerability scanner
|
||||||
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 # v0.30.0
|
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 # v0.32.0
|
||||||
|
id: trivy_scan
|
||||||
with:
|
with:
|
||||||
image-ref: ${{ inputs.image_ref }}
|
image-ref: ${{ inputs.image_ref }}
|
||||||
format: 'json'
|
format: 'json'
|
||||||
output: 'trivy-results.json'
|
output: 'trivy-results.json'
|
||||||
severity: 'CRITICAL,HIGH,MEDIUM,LOW'
|
severity: 'CRITICAL,HIGH'
|
||||||
vuln-type: 'os,library'
|
|
||||||
ignore-unfixed: false
|
ignore-unfixed: false
|
||||||
exit-code: '0'
|
exit-code: '0'
|
||||||
|
|
||||||
- name: Process vulnerability results
|
- name: Calculate vulnerability counts
|
||||||
id: process_vulns
|
id: process_results
|
||||||
run: |
|
run: |
|
||||||
if [ -f trivy-results.json ]; then
|
if [ ! -s trivy-results.json ] || [ $(jq '.Results | length' trivy-results.json) -eq 0 ]; then
|
||||||
CRITICAL_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' trivy-results.json)
|
echo "No high-severity vulnerabilities found."
|
||||||
HIGH_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' trivy-results.json)
|
|
||||||
MEDIUM_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "MEDIUM")] | length' trivy-results.json)
|
|
||||||
LOW_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "LOW")] | length' trivy-results.json)
|
|
||||||
|
|
||||||
TOTAL_VULNS=$((CRITICAL_COUNT + HIGH_COUNT + MEDIUM_COUNT + LOW_COUNT))
|
|
||||||
|
|
||||||
echo "critical_count=${CRITICAL_COUNT}" >> $GITHUB_OUTPUT
|
|
||||||
echo "high_count=${HIGH_COUNT}" >> $GITHUB_OUTPUT
|
|
||||||
echo "medium_count=${MEDIUM_COUNT}" >> $GITHUB_OUTPUT
|
|
||||||
echo "low_count=${LOW_COUNT}" >> $GITHUB_OUTPUT
|
|
||||||
echo "total_count=${TOTAL_VULNS}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
if [ $TOTAL_VULNS -gt 0 ]; then
|
|
||||||
echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Extract top vulnerabilities for display (limit to 10 for readability)
|
|
||||||
TOP_VULNS=$(jq -r '
|
|
||||||
.Results[]?
|
|
||||||
| .Vulnerabilities[]?
|
|
||||||
| select(.Severity == "CRITICAL" or .Severity == "HIGH" or .Severity == "MEDIUM" or .Severity == "LOW")
|
|
||||||
| "• \(.VulnerabilityID): \(.Title // "No title") (\(.Severity))"
|
|
||||||
' trivy-results.json | head -10)
|
|
||||||
|
|
||||||
echo "top_vulnerabilities<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "$TOP_VULNS" >> $GITHUB_OUTPUT
|
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "vulnerabilities_found=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Trivy results file not found."
|
|
||||||
echo "vulnerabilities_found=false" >> $GITHUB_OUTPUT
|
echo "vulnerabilities_found=false" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Notify Slack - Vulnerabilities Found
|
# Calculate counts by severity
|
||||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
CRITICAL_COUNT=$(jq '([.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length)' trivy-results.json)
|
||||||
if: steps.process_vulns.outputs.vulnerabilities_found == 'true'
|
HIGH_COUNT=$(jq '([.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length)' trivy-results.json)
|
||||||
|
TOTAL_VULNS=$((CRITICAL_COUNT + HIGH_COUNT))
|
||||||
|
|
||||||
|
# Get unique CVE count
|
||||||
|
UNIQUE_CVES=$(jq -r '[.Results[]?.Vulnerabilities[]?.VulnerabilityID] | unique | length' trivy-results.json)
|
||||||
|
|
||||||
|
# Get affected packages count
|
||||||
|
AFFECTED_PACKAGES=$(jq -r '[.Results[]?.Vulnerabilities[]? | .PkgName] | unique | length' trivy-results.json)
|
||||||
|
|
||||||
|
echo "vulnerabilities_found=$( [ $TOTAL_VULNS -gt 0 ] && echo 'true' || echo 'false' )" >> $GITHUB_OUTPUT
|
||||||
|
echo "total_count=$TOTAL_VULNS" >> $GITHUB_OUTPUT
|
||||||
|
echo "critical_count=$CRITICAL_COUNT" >> $GITHUB_OUTPUT
|
||||||
|
echo "high_count=$HIGH_COUNT" >> $GITHUB_OUTPUT
|
||||||
|
echo "unique_cves=$UNIQUE_CVES" >> $GITHUB_OUTPUT
|
||||||
|
echo "affected_packages=$AFFECTED_PACKAGES" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Generate GitHub Job Summary
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
{
|
||||||
|
echo "# 🛡️ Trivy Security Scan Results"
|
||||||
|
echo ""
|
||||||
|
echo "**Image:** \`${{ inputs.image_ref }}\`"
|
||||||
|
echo "**Scan Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||||
|
echo ""
|
||||||
|
} >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
if [ "${{ steps.process_results.outputs.vulnerabilities_found }}" == "false" ]; then
|
||||||
|
echo "✅ **No critical or high severity vulnerabilities found!**" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "## 📊 Summary"
|
||||||
|
echo "| Metric | Count |"
|
||||||
|
echo "|--------|-------|"
|
||||||
|
echo "| 🔴 Critical Vulnerabilities | ${{ steps.process_results.outputs.critical_count }} |"
|
||||||
|
echo "| 🟠 High Vulnerabilities | ${{ steps.process_results.outputs.high_count }} |"
|
||||||
|
echo "| 📋 Unique CVEs | ${{ steps.process_results.outputs.unique_cves }} |"
|
||||||
|
echo "| 📦 Affected Packages | ${{ steps.process_results.outputs.affected_packages }} |"
|
||||||
|
echo ""
|
||||||
|
echo "## 🚨 Top Vulnerabilities"
|
||||||
|
echo ""
|
||||||
|
} >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Generate detailed vulnerability table
|
||||||
|
jq -r --arg image_ref "${{ inputs.image_ref }}" '
|
||||||
|
# Collect all vulnerabilities
|
||||||
|
[.Results[] | select(.Vulnerabilities != null) | .Vulnerabilities[]] |
|
||||||
|
# Group by CVE ID to avoid duplicates
|
||||||
|
group_by(.VulnerabilityID) |
|
||||||
|
map({
|
||||||
|
cve: .[0].VulnerabilityID,
|
||||||
|
severity: .[0].Severity,
|
||||||
|
cvss: (.[0].CVSS.nvd.V3Score // "N/A"),
|
||||||
|
cvss_sort: (.[0].CVSS.nvd.V3Score // 0),
|
||||||
|
packages: [.[] | "\(.PkgName)@\(.InstalledVersion)"] | unique | join(", "),
|
||||||
|
fixed: (.[0].FixedVersion // "No fix available"),
|
||||||
|
description: (.[0].Description // "No description available") | split("\n")[0] | .[0:150]
|
||||||
|
}) |
|
||||||
|
# Sort by severity (CRITICAL first) and CVSS score
|
||||||
|
sort_by((.severity == "HIGH" | if . then 1 else 0 end), -.cvss_sort) |
|
||||||
|
# Take top 15
|
||||||
|
.[:15] |
|
||||||
|
# Generate markdown table
|
||||||
|
"| CVE | Severity | CVSS | Package(s) | Fix Version | Description |",
|
||||||
|
"|-----|----------|------|------------|-------------|-------------|",
|
||||||
|
(.[] | "| [\(.cve)](https://nvd.nist.gov/vuln/detail/\(.cve)) | \(.severity) | \(.cvss) | `\(.packages)` | `\(.fixed)` | \(.description) |")
|
||||||
|
' trivy-results.json >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "---"
|
||||||
|
echo "🔍 **View detailed logs above for full analysis**"
|
||||||
|
} >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Generate Slack Blocks JSON
|
||||||
|
if: steps.process_results.outputs.vulnerabilities_found == 'true'
|
||||||
|
id: generate_blocks
|
||||||
|
run: |
|
||||||
|
BLOCKS_JSON=$(jq -c --arg image_ref "${{ inputs.image_ref }}" \
|
||||||
|
--arg repo_url "${{ github.server_url }}/${{ github.repository }}" \
|
||||||
|
--arg repo_name "${{ github.repository }}" \
|
||||||
|
--arg run_url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
||||||
|
--arg critical_count "${{ steps.process_results.outputs.critical_count }}" \
|
||||||
|
--arg high_count "${{ steps.process_results.outputs.high_count }}" \
|
||||||
|
--arg unique_cves "${{ steps.process_results.outputs.unique_cves }}" \
|
||||||
|
'
|
||||||
|
# Function to create a vulnerability block with emoji indicators
|
||||||
|
def vuln_block: {
|
||||||
|
"type": "section",
|
||||||
|
"text": {
|
||||||
|
"type": "mrkdwn",
|
||||||
|
"text": "\(if .Severity == "CRITICAL" then ":red_circle:" else ":large_orange_circle:" end) *<https://nvd.nist.gov/vuln/detail/\(.VulnerabilityID)|\(.VulnerabilityID)>* (CVSS: `\(.CVSS.nvd.V3Score // "N/A")`)\n*Package:* `\(.PkgName)@\(.InstalledVersion)` → `\(.FixedVersion // "No fix available")`"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
# Main structure
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "header",
|
||||||
|
"text": { "type": "plain_text", "text": ":warning: Trivy Scan: Vulnerabilities Detected" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "section",
|
||||||
|
"fields": [
|
||||||
|
{ "type": "mrkdwn", "text": "*Repository:*\n<\($repo_url)|\($repo_name)>" },
|
||||||
|
{ "type": "mrkdwn", "text": "*Image:*\n`\($image_ref)`" },
|
||||||
|
{ "type": "mrkdwn", "text": "*Critical:*\n:red_circle: \($critical_count)" },
|
||||||
|
{ "type": "mrkdwn", "text": "*High:*\n:large_orange_circle: \($high_count)" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "context",
|
||||||
|
"elements": [
|
||||||
|
{ "type": "mrkdwn", "text": ":shield: \($unique_cves) unique CVEs affecting packages" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ "type": "divider" }
|
||||||
|
] +
|
||||||
|
(
|
||||||
|
# Group vulnerabilities by CVE to avoid duplicates in notification
|
||||||
|
[.Results[] | select(.Vulnerabilities != null) | .Vulnerabilities[]] |
|
||||||
|
group_by(.VulnerabilityID) |
|
||||||
|
map(.[0]) |
|
||||||
|
sort_by((.Severity == "HIGH" | if . then 1 else 0 end), -((.CVSS.nvd.V3Score // 0) | tonumber? // 0)) |
|
||||||
|
.[:8] |
|
||||||
|
map(. | vuln_block)
|
||||||
|
) +
|
||||||
|
[
|
||||||
|
{ "type": "divider" },
|
||||||
|
{
|
||||||
|
"type": "actions",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "button",
|
||||||
|
"text": { "type": "plain_text", "text": ":github: View Full Report" },
|
||||||
|
"style": "primary",
|
||||||
|
"url": $run_url
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
' trivy-results.json)
|
||||||
|
|
||||||
|
echo "slack_blocks=$BLOCKS_JSON" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Send Slack Notification
|
||||||
|
if: steps.process_results.outputs.vulnerabilities_found == 'true'
|
||||||
|
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||||
with:
|
with:
|
||||||
status: 'warning'
|
method: chat.postMessage
|
||||||
channel: '#mission-security'
|
token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
payload: |
|
||||||
message: |
|
channel: ${{ env.SLACK_CHANNEL_ID }}
|
||||||
:warning: *Trivy Scan: Vulnerabilities Detected*
|
text: "🚨 Trivy Scan: ${{ steps.process_results.outputs.critical_count }} Critical, ${{ steps.process_results.outputs.high_count }} High vulnerabilities found in ${{ inputs.image_ref }}"
|
||||||
|
blocks: ${{ steps.generate_blocks.outputs.slack_blocks }}
|
||||||
*Repository:* `${{ github.repository }}`
|
|
||||||
*Image Ref:* `${{ inputs.image_ref }}`
|
|
||||||
|
|
||||||
*Vulnerability Summary:*
|
|
||||||
• *Critical:* ${{ steps.process_vulns.outputs.critical_count }}
|
|
||||||
• *High:* ${{ steps.process_vulns.outputs.high_count }}
|
|
||||||
• *Medium:* ${{ steps.process_vulns.outputs.medium_count }}
|
|
||||||
• *Low:* ${{ steps.process_vulns.outputs.low_count }}
|
|
||||||
• *Total:* ${{ steps.process_vulns.outputs.total_count }}
|
|
||||||
|
|
||||||
*Top Vulnerabilities (showing first 10):*
|
|
||||||
```
|
|
||||||
${{ steps.process_vulns.outputs.top_vulnerabilities }}
|
|
||||||
```
|
|
||||||
|
|
||||||
:point_right: <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Full Scan Results>
|
|
||||||
|
|
||||||
- name: Notify Slack - No Vulnerabilities
|
|
||||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
|
||||||
if: steps.process_vulns.outputs.vulnerabilities_found == 'false'
|
|
||||||
with:
|
|
||||||
status: 'success'
|
|
||||||
channel: '#mission-security'
|
|
||||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
message: |
|
|
||||||
:white_check_mark: *Trivy Scan: All Clear*
|
|
||||||
|
|
||||||
*Repository:* `${{ github.repository }}`
|
|
||||||
*Image Ref:* `${{ inputs.image_ref }}`
|
|
||||||
|
|
||||||
No vulnerabilities detected in the Docker image scan.
|
|
||||||
|
|
||||||
:point_right: <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Scan Results>
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,3 +35,4 @@ compiled_app_output
|
|||||||
trivy_report*
|
trivy_report*
|
||||||
compiled
|
compiled
|
||||||
packages/cli/src/modules/my-feature
|
packages/cli/src/modules/my-feature
|
||||||
|
.secrets
|
||||||
|
|||||||
Reference in New Issue
Block a user