Trivy Nightly Security Scan #320
This file contains hidden or 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
| name: Trivy Nightly Security Scan | |
| on: | |
| workflow_call: | |
| inputs: | |
| commit_id: | |
| required: false | |
| type: string | |
| workflow_dispatch: | |
| schedule: | |
| - cron: '0 0 * * *' # Runs daily at midnight UTC | |
| jobs: | |
| security-scan: | |
| if: github.event.pull_request.draft == false | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| packages: read | |
| issues: write | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 45 | |
| env: | |
| TRIVY_VERSION: 0.50.1 | |
| COMMIT_ID: ${{ inputs.commit_id || github.sha }} | |
| steps: | |
| # ============ SETUP PHASE ============ | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.COMMIT_ID }} | |
| fetch-depth: 0 | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.10' | |
| # ============ SCANNING PHASE ============ | |
| - name: Run filesystem scan | |
| uses: aquasecurity/trivy-action@0.30.0 | |
| with: | |
| scan-type: 'fs' | |
| format: 'json' | |
| output: 'trivy-fs-report.json' | |
| severity: 'CRITICAL,HIGH' | |
| ignore-unfixed: true | |
| vuln-type: 'os,library' | |
| security-checks: 'vuln' | |
| - name: Build Docker image | |
| run: | | |
| docker buildx build \ | |
| --pull \ | |
| --tag local/scan-target:${{ github.run_id }} \ | |
| --file openfl-docker/Dockerfile.base \ | |
| --load \ | |
| . | |
| - name: Scan Docker image | |
| uses: aquasecurity/trivy-action@0.30.0 | |
| with: | |
| image-ref: 'local/scan-target:${{ github.run_id }}' | |
| format: 'json' | |
| output: 'trivy-image-report.json' | |
| severity: 'CRITICAL,HIGH' | |
| ignore-unfixed: true | |
| vuln-type: 'os,library' | |
| security-checks: 'vuln' | |
| # ============ REPORTING PHASE ============ | |
| - name: Generate SBOM reports | |
| run: | | |
| trivy fs --format spdx-json --output trivy-fs-sbom.json . | |
| trivy image --format spdx-json --output trivy-image-sbom.json local/scan-target:${{ github.run_id }} | |
| - name: Create consolidated report | |
| id: report | |
| run: | | |
| # Initialize markdown report | |
| echo "# Security Scan Report - OpenFL" > report.md | |
| echo "**Scan Date:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> report.md | |
| echo "**Commit:** [${{ env.COMMIT_ID }}](https://github.com/rajithkrishnegowda/openfl/commit/${{ env.COMMIT_ID }})" >> report.md | |
| echo -e "\n## Vulnerability Summary\n" >> report.md | |
| # Process filesystem results | |
| if [ -f "trivy-fs-report.json" ]; then | |
| FS_VULNS=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-fs-report.json || echo 0) | |
| echo "### Filesystem Scans" >> report.md | |
| echo "**Critical/High Vulnerabilities:** $FS_VULNS" >> report.md | |
| if [ "$FS_VULNS" -gt 0 ]; then | |
| echo -e "\n| Severity | ID | Package | Version | Description |" >> report.md | |
| echo "|----------|----|---------|---------|-------------|" >> report.md | |
| jq -r '.Results[]?.Vulnerabilities[]? | "| \(.Severity) | \(.VulnerabilityID) | \(.PkgName) | \(.InstalledVersion) | \(.Title) |"' trivy-fs-report.json >> report.md | |
| echo "has_vulnerabilities=true" >> $GITHUB_OUTPUT | |
| fi | |
| fi | |
| # Process image results | |
| if [ -f "trivy-image-report.json" ]; then | |
| IMG_VULNS=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-image-report.json || echo 0) | |
| echo -e "\n### Container Image Scans" >> report.md | |
| echo "**Critical/High Vulnerabilities:** $IMG_VULNS" >> report.md | |
| if [ "$IMG_VULNS" -gt 0 ]; then | |
| echo -e "\n| Severity | ID | Package | Version | Description |" >> report.md | |
| echo "|----------|----|---------|---------|-------------|" >> report.md | |
| jq -r '.Results[]?.Vulnerabilities[]? | "| \(.Severity) | \(.VulnerabilityID) | \(.PkgName) | \(.InstalledVersion) | \(.Title) |"' trivy-image-report.json >> report.md | |
| echo "has_vulnerabilities=true" >> $GITHUB_OUTPUT | |
| fi | |
| fi | |
| # Add artifact download links | |
| echo -e "\n## Next Steps\n" >> report.md | |
| echo "1. Review the full reports in the workflow artifacts" >> report.md | |
| echo "2. Address critical vulnerabilities immediately" >> report.md | |
| echo "3. Create GitHub issues for tracking remediation" >> report.md | |
| cat report.md | |
| # ============ NOTIFICATION PHASE ============ | |
| - name: Set notification subject | |
| id: set-subject | |
| run: | | |
| if [[ "${{ job.status }}" == "failure" ]]; then | |
| echo "subject=🚨 OpenFL Security Scan Failed" >> $GITHUB_OUTPUT | |
| elif [[ "${{ steps.report.outputs.has_vulnerabilities }}" == "true" ]]; then | |
| echo "subject=⚠️ OpenFL Vulnerabilities Found" >> $GITHUB_OUTPUT | |
| else | |
| echo "subject=✅ OpenFL Security Scan Passed" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Extract CODEOWNERS emails | |
| id: codeowners | |
| run: | | |
| if ! command -v python &> /dev/null; then | |
| sudo apt-get update && sudo apt-get install -y python3 | |
| fi | |
| OUTPUT=$(python .github/scripts/extract_emails.py) | |
| echo "Extracted emails: $OUTPUT" | |
| EMAILS=$(echo "$OUTPUT" | jq -r '.emails | join(",")') | |
| echo "emails=${EMAILS:-${{ secrets.SECURITY_EMAIL_RECIPIENTS }}}" >> $GITHUB_OUTPUT | |
| env: | |
| PYTHONIOENCODING: utf-8 | |
| - name: Prepare email content | |
| id: prepare-email | |
| run: | | |
| # Convert markdown to HTML | |
| python -m pip install markdown | |
| HTML_CONTENT=$(python -c "import markdown; print(markdown.markdown(open('report.md').read()))") | |
| echo "html_body<<EOF" >> $GITHUB_OUTPUT | |
| echo "$HTML_CONTENT" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| - name: Send email via Python script | |
| if: always() && (steps.report.outputs.has_vulnerabilities == 'true' || failure()) | |
| env: | |
| SMTP_SERVER: ${{ secrets.SMTP_SERVER }} | |
| SMTP_PORT: ${{ secrets.SMTP_PORT }} | |
| SMTP_USER: ${{ secrets.SMTP_USER }} | |
| SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} | |
| RECIPIENTS: ${{ steps.codeowners.outputs.emails }} | |
| run: | | |
| python .github/scripts/send_email.py \ | |
| --sender "security@openfl.github" \ | |
| --to "$RECIPIENTS" \ | |
| --subject "${{ steps.set-subject.outputs.subject }}" \ | |
| --body "${{ steps.prepare-email.outputs.html_body }}" \ | |
| --smtp-user "$SMTP_USER" \ | |
| --smtp-pwd "$SMTP_PASSWORD" \ | |
| --smtp-server "$SMTP_SERVER:$SMTP_PORT" \ | |
| --html-body | |
| # ============ ARTIFACT UPLOADS ============ | |
| - name: Upload scan artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-reports-${{ github.run_id }} | |
| path: | | |
| trivy-fs-report.json | |
| trivy-image-report.json | |
| trivy-fs-sbom.json | |
| trivy-image-sbom.json | |
| report.md | |
| retention-days: 30 | |
| # ============ FAILURE HANDLING ============ | |
| - name: Fail workflow if vulnerabilities found | |
| if: steps.report.outputs.has_vulnerabilities == 'true' && github.event_name != 'schedule' | |
| run: | | |
| echo "::error::Critical/High vulnerabilities detected!" | |
| exit 1 |