From 7ae67f016d2ce621ddde3ea59200fc676f1cee5a Mon Sep 17 00:00:00 2001 From: shortstacked Date: Mon, 2 Jun 2025 12:24:50 +0100 Subject: [PATCH] ci: Add Trivy Scan as notification to release/nightly (no-changelog) (#15868) --- .../workflows/check-documentation-urls.yml | 2 +- .github/workflows/ci-master.yml | 2 +- .github/workflows/ci-postgres-mysql.yml | 2 +- .github/workflows/docker-images-nightly.yml | 8 ++ .github/workflows/e2e-tests.yml | 2 +- .github/workflows/release-publish.yml | 9 ++ .../security-trivy-scan-callable.yml | 123 ++++++++++++++++++ 7 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/security-trivy-scan-callable.yml diff --git a/.github/workflows/check-documentation-urls.yml b/.github/workflows/check-documentation-urls.yml index 0d59245619..775259de93 100644 --- a/.github/workflows/check-documentation-urls.yml +++ b/.github/workflows/check-documentation-urls.yml @@ -37,7 +37,7 @@ jobs: run: node .github/scripts/validate-docs-links.js - name: Notify Slack on failure - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() with: status: ${{ job.status }} diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml index 5345632dbd..6e94fd7abc 100644 --- a/.github/workflows/ci-master.yml +++ b/.github/workflows/ci-master.yml @@ -71,7 +71,7 @@ jobs: needs: [unit-test, lint] steps: - name: Notify Slack on failure - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() with: status: ${{ job.status }} diff --git a/.github/workflows/ci-postgres-mysql.yml b/.github/workflows/ci-postgres-mysql.yml index 40db66217c..3e308d2748 100644 --- a/.github/workflows/ci-postgres-mysql.yml +++ b/.github/workflows/ci-postgres-mysql.yml @@ -220,7 +220,7 @@ jobs: needs: [mariadb, postgres, mysql] steps: - name: Notify Slack on failure - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() && github.ref == 'refs/heads/master' with: status: ${{ job.status }} diff --git a/.github/workflows/docker-images-nightly.yml b/.github/workflows/docker-images-nightly.yml index 96b07578a3..427d906d10 100644 --- a/.github/workflows/docker-images-nightly.yml +++ b/.github/workflows/docker-images-nightly.yml @@ -48,3 +48,11 @@ jobs: tags: | ghcr.io/${{ github.repository_owner }}/n8n:nightly ${{ secrets.DOCKER_USERNAME }}/n8n:nightly + + security-scan: + needs: build + uses: ./.github/workflows/security-trivy-scan-callable.yml + with: + image_ref: ghcr.io/${{ github.repository_owner }}/n8n:nightly + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 8749cac0c9..fa67221b8f 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -56,7 +56,7 @@ jobs: if: ${{ github.event.inputs.success-url != '' }} steps: - name: Notify Slack on failure - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() with: status: ${{ job.status }} diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 6b91b933f8..1e36c6de4d 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -120,6 +120,15 @@ jobs: ${{ secrets.DOCKER_USERNAME }}/n8n:${{ needs.publish-to-npm.outputs.release }} ghcr.io/${{ github.repository_owner }}/n8n:${{ needs.publish-to-npm.outputs.release }} + security-scan: + name: Security Scan Release Image + needs: [publish-to-npm, publish-to-docker-hub] + uses: ./.github/workflows/security-trivy-scan-callable.yml + with: + image_ref: ghcr.io/${{ github.repository_owner }}/n8n:${{ needs.publish-to-npm.outputs.release }} + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + create-github-release: name: Create a GitHub Release needs: [publish-to-npm, publish-to-docker-hub] diff --git a/.github/workflows/security-trivy-scan-callable.yml b/.github/workflows/security-trivy-scan-callable.yml new file mode 100644 index 0000000000..e4cbc536c1 --- /dev/null +++ b/.github/workflows/security-trivy-scan-callable.yml @@ -0,0 +1,123 @@ +name: Security - Scan Docker Image With Trivy + +on: + workflow_dispatch: + inputs: + image_ref: + description: Full image reference to scan e.g ghcr.io/n8n-io/n8n:latest + required: true + default: 'ghcr.io/n8n-io/n8n:latest' + workflow_call: + inputs: + image_ref: + type: string + description: Full image reference to scan e.g ghcr.io/n8n-io/n8n:latest + required: true + secrets: + SLACK_WEBHOOK_URL: + required: true + +jobs: + security_scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Pull Docker image + run: | + docker pull ${{ inputs.image_ref }} + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@6c175e9c4083a92bbca2f9724c8a5e33bc2d97a5 # v0.30.0 + with: + image-ref: ${{ inputs.image_ref }} + format: 'json' + output: 'trivy-results.json' + severity: 'CRITICAL,HIGH,MEDIUM,LOW' + vuln-type: 'os,library' + ignore-unfixed: false + exit-code: '0' + + - name: Process vulnerability results + id: process_vulns + run: | + if [ -f trivy-results.json ]; then + CRITICAL_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' trivy-results.json) + 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<> $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 + fi + + - name: Notify Slack - Vulnerabilities Found + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 + if: steps.process_vulns.outputs.vulnerabilities_found == 'true' + with: + status: 'warning' + channel: '#mission-security' + webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} + message: | + :warning: *Trivy Scan: Vulnerabilities Detected* + + *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>