Security Scan #30
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: | |
| schedule: | |
| - cron: '0 2 * * 1' # Run at 2 AM every Monday | |
| workflow_dispatch: # Allow manual triggering | |
| push: | |
| branches: | |
| - main | |
| - 'release-*' | |
| paths: | |
| - '**/*.js' | |
| - '**/*.jsx' | |
| - '**/*.ts' | |
| - '**/*.tsx' | |
| - 'package*.json' | |
| permissions: | |
| contents: read | |
| security-events: write | |
| issues: write | |
| jobs: | |
| security-scan: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| # Generate Software Bill of Materials (SBOM) | |
| - name: Generate SBOM with CycloneDX | |
| uses: CycloneDX/gh-node-module-generatebom@v1 | |
| with: | |
| path: ./ | |
| output: cyclonedx.json | |
| - name: Generate SBOM with Syft | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| format: spdx-json | |
| output-file: ./syft-sbom.json | |
| # Upload SBOM files | |
| - name: Upload SBOM files | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom-files | |
| path: | | |
| cyclonedx.json | |
| syft-sbom.json | |
| retention-days: 90 | |
| timeout-minutes: 5 # Prevent artifact upload from hanging | |
| # Run npm audit to check for vulnerable dependencies | |
| - name: Run npm audit | |
| run: npm audit --production | |
| continue-on-error: true | |
| # Run Snyk vulnerability scan | |
| - name: Run Snyk to check for vulnerabilities | |
| uses: snyk/actions/node@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| args: --severity-threshold=high | |
| # Run Trivy vulnerability scanner | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| - name: Upload Trivy scan results to GitHub Security tab | |
| uses: github/codeql-action/upload-sarif@v2 | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| # Static code analysis with ESLint security plugins | |
| - name: Install ESLint and security plugins | |
| run: | | |
| npm install -g eslint | |
| npm install -g eslint-plugin-security eslint-plugin-sonarjs eslint-plugin-node | |
| - name: Run ESLint security scan | |
| run: | | |
| eslint --plugin security,sonarjs --ext .js,.jsx,.ts,.tsx src/ server/ --no-eslintrc --config .github/workflows/eslint-security.json -f json > eslint-report.json || true | |
| # Secret scanning | |
| - name: Secret Scanning with GitLeaks | |
| uses: gitleaks/gitleaks-action@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} | |
| continue-on-error: true | |
| # Check for dependency confusion vulnerabilities | |
| - name: Dependency Confusion Check | |
| run: | | |
| npm install -g @jwt/confusion-scanner | |
| npx @jwt/confusion-scanner --path ./ --output dependency-confusion-report.json | |
| continue-on-error: true | |
| # Check package.json for issues | |
| - name: Package.json security audit | |
| uses: lirantal/lockfile-lint-action@v4.7.1 | |
| with: | |
| path: package-lock.json | |
| allowed-hosts: npm | |
| validate-https: true | |
| allowed-schemes: "https:" | |
| continue-on-error: true | |
| # Run NodeJsScan for security issues | |
| - name: Install NodeJsScan | |
| run: pip install nodejsscan | |
| - name: Run NodeJsScan | |
| run: nodejsscan -d ./ -o nodejsscan-report.json | |
| continue-on-error: true | |
| # OWASP ZAP Baseline Scan | |
| - name: ZAP Baseline Scan | |
| uses: zaproxy/action-baseline@v0.9.0 | |
| with: | |
| target: 'http://localhost:3000' | |
| rules_file_name: '.github/workflows/zap-rules.tsv' | |
| cmd_options: '-a' | |
| env: | |
| API_URL: 'http://localhost:5000/api' | |
| # Upload scan results as artifacts | |
| - name: Upload scan results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-scan-reports | |
| path: | | |
| eslint-report.json | |
| nodejsscan-report.json | |
| zap-report.html | |
| trivy-results.sarif | |
| dependency-confusion-report.json | |
| retention-days: 30 | |
| timeout-minutes: 5 # Prevent artifact upload from hanging | |
| # License compliance scanning | |
| - name: License Scanning | |
| uses: fossas/fossa-action@main | |
| with: | |
| api-key: ${{ secrets.FOSSA_API_KEY }} | |
| continue-on-error: true | |
| # Notify on critical findings | |
| - name: Check for critical vulnerabilities | |
| id: check_critical | |
| run: | | |
| critical_count=$(grep -c "CRITICAL" trivy-results.sarif || echo "0") | |
| if [ "$critical_count" -gt "0" ]; then | |
| echo "has_critical=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_critical=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Create GitHub issue for critical vulnerabilities | |
| if: steps.check_critical.outputs.has_critical == 'true' | |
| uses: actions/github-script@v6 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: '🚨 Critical Security Vulnerabilities Detected', | |
| body: 'The security scan has detected critical vulnerabilities. Please check the security scan artifacts in GitHub Actions run #' + context.runId, | |
| labels: ['security', 'critical'] | |
| }) |