Security Scan #599
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: Security Scan | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| schedule: | |
| # Run weekly on Sundays at 2 AM UTC | |
| - cron: '0 2 * * 0' | |
| workflow_dispatch: # Allow manual triggering | |
| jobs: | |
| # ============================================================================ | |
| # SECRET SCANNING | |
| # ============================================================================ | |
| gitleaks: | |
| name: Gitleaks Secret Scanning | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 # Fetch all history for all branches | |
| - name: Run Gitleaks | |
| uses: gitleaks/gitleaks-action@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITLEAKS_CONFIG: .gitleaks.toml | |
| GITLEAKS_ENABLE_COMMENTS: true | |
| GITLEAKS_NOTIFY_USER_LIST: "@mbuckingham74" | |
| - name: Upload Gitleaks SARIF report | |
| if: failure() | |
| uses: github/codeql-action/upload-sarif@v4 | |
| with: | |
| sarif_file: results.sarif | |
| category: gitleaks | |
| - name: Summary | |
| if: always() | |
| run: | | |
| echo "## 🔒 Gitleaks Security Scan" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ job.status }}" == "success" ]; then | |
| echo "✅ No secrets detected in repository" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Potential secrets detected! Review the findings above." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # ============================================================================ | |
| # NPM AUDIT | |
| # ============================================================================ | |
| npm-audit: | |
| name: NPM Security Audit | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| workspace: [frontend, backend] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '20.x' | |
| cache: 'npm' | |
| cache-dependency-path: ${{ matrix.workspace }}/package-lock.json | |
| - name: Install dependencies | |
| run: | | |
| cd ${{ matrix.workspace }} | |
| npm ci | |
| - name: Run npm audit | |
| id: audit | |
| run: | | |
| cd ${{ matrix.workspace }} | |
| npm audit --production --audit-level=moderate || AUDIT_FAILED=true | |
| # Save audit results | |
| npm audit --json --production > audit-results.json || true | |
| if [ "$AUDIT_FAILED" == "true" ]; then | |
| echo "status=failed" >> $GITHUB_OUTPUT | |
| else | |
| echo "status=passed" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Upload audit results | |
| if: always() | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: npm-audit-${{ matrix.workspace }} | |
| path: ${{ matrix.workspace }}/audit-results.json | |
| retention-days: 30 | |
| - name: Parse audit results | |
| if: always() | |
| run: | | |
| cd ${{ matrix.workspace }} | |
| if [ -f audit-results.json ]; then | |
| CRITICAL=$(cat audit-results.json | jq '.metadata.vulnerabilities.critical // 0') | |
| HIGH=$(cat audit-results.json | jq '.metadata.vulnerabilities.high // 0') | |
| MODERATE=$(cat audit-results.json | jq '.metadata.vulnerabilities.moderate // 0') | |
| LOW=$(cat audit-results.json | jq '.metadata.vulnerabilities.low // 0') | |
| echo "## 🔍 NPM Audit - ${{ matrix.workspace }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Critical | $CRITICAL |" >> $GITHUB_STEP_SUMMARY | |
| echo "| High | $HIGH |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Moderate | $MODERATE |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Low | $LOW |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| TOTAL=$((CRITICAL + HIGH + MODERATE + LOW)) | |
| if [ $TOTAL -eq 0 ]; then | |
| echo "✅ No vulnerabilities found" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Found $TOTAL vulnerabilities" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| fi | |
| - name: Fail on critical/high vulnerabilities | |
| if: steps.audit.outputs.status == 'failed' | |
| run: | | |
| echo "❌ Security audit failed - Critical or high vulnerabilities detected" | |
| exit 1 | |
| # ============================================================================ | |
| # DEPENDENCY REVIEW (PR only) | |
| # ============================================================================ | |
| dependency-review: | |
| name: Dependency Review | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Dependency Review | |
| uses: actions/dependency-review-action@v4 | |
| with: | |
| fail-on-severity: moderate | |
| deny-licenses: GPL-3.0, AGPL-3.0 | |
| # ============================================================================ | |
| # FINAL STATUS | |
| # ============================================================================ | |
| security-success: | |
| name: Security Scan Success | |
| runs-on: ubuntu-latest | |
| needs: [gitleaks, npm-audit] | |
| if: always() | |
| steps: | |
| - name: Check all security scans | |
| run: | | |
| if [ "${{ contains(needs.*.result, 'failure') }}" == "true" ]; then | |
| echo "❌ One or more security scans failed" | |
| exit 1 | |
| fi | |
| echo "✅ All security scans passed!" | |
| - name: Summary | |
| run: | | |
| echo "## ✅ Security Scan Complete" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "All security checks passed:" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ No secrets detected (Gitleaks)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ No critical vulnerabilities (NPM Audit)" >> $GITHUB_STEP_SUMMARY |