Security Scan #626
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] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| # Run daily at 9 AM UTC | |
| - cron: '0 9 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| security-events: write | |
| pull-requests: write # Needed for PR comments | |
| jobs: | |
| # CodeQL analysis for JavaScript/TypeScript | |
| codeql: | |
| name: CodeQL Analysis | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: read | |
| security-events: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| language: ['javascript-typescript'] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v3 | |
| with: | |
| languages: ${{ matrix.language }} | |
| queries: security-extended,security-and-quality | |
| - name: Build project | |
| run: | | |
| npm ci | |
| npm run build | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v3 | |
| with: | |
| category: "/language:${{ matrix.language }}" | |
| # Content validation security scan | |
| content-scan: | |
| name: Content 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: '22.x' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build project | |
| run: npm run build | |
| - name: Run content security scan | |
| id: content-scan | |
| shell: bash | |
| run: | | |
| echo "🔒 Scanning content for security patterns..." | |
| # Find all content files | |
| FILES=$(find library showcase catalog -name "*.md" -type f 2>/dev/null | tr '\n' ' ') | |
| if [ -z "$FILES" ]; then | |
| echo "No content files found to scan" | |
| exit 0 | |
| fi | |
| # Run the content validator on all content files | |
| if node dist/src/cli/validate-content.js $FILES; then | |
| echo "✅ Content security scan passed" | |
| else | |
| echo "SCAN_FAILED=true" >> $GITHUB_ENV | |
| fi | |
| - name: Upload scan results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-scan-results | |
| path: scan-results/ | |
| - name: Comment PR with results | |
| if: github.event_name == 'pull_request' && always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| let report = '## 🔒 Content Security Scan Results\n\n'; | |
| const failed = process.env.SCAN_FAILED === 'true'; | |
| if (failed) { | |
| report += '### ❌ Security Issues Found\n\n'; | |
| report += 'Critical security patterns were detected in the content. Please review the scan results.\n'; | |
| } else { | |
| report += '### ✅ Security Scan Passed\n\n'; | |
| report += 'No critical security issues found in the content.\n'; | |
| } | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: report | |
| }); | |
| - name: Fail if critical issues found | |
| if: env.SCAN_FAILED == 'true' | |
| run: | | |
| echo "❌ Security scan found critical issues. Please review the report." | |
| exit 1 | |
| # Dependency vulnerability scan | |
| dependency-scan: | |
| name: Dependency Vulnerability Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run npm audit | |
| run: | | |
| npm audit --production --audit-level=moderate || echo "AUDIT_FAILED=true" >> $GITHUB_ENV | |
| - name: Upload audit report | |
| if: always() | |
| run: | | |
| # Capture exit code without tripping `bash -e`. `npm audit` exits 1 | |
| # whenever advisories exist (even low-severity), so we must use | |
| # `||` to suppress -e and still capture the code for the threshold | |
| # check. Only exit codes > 1 indicate a genuine audit error. | |
| AUDIT_EXIT=0 | |
| npm audit --json > npm-audit.json || AUDIT_EXIT=$? | |
| if [ "$AUDIT_EXIT" -gt 1 ]; then | |
| echo "npm audit encountered an error (exit code: $AUDIT_EXIT)" | |
| exit "$AUDIT_EXIT" | |
| fi | |
| echo "### NPM Audit Report" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`json" >> $GITHUB_STEP_SUMMARY | |
| cat npm-audit.json >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| - name: Fail if vulnerabilities found | |
| if: env.AUDIT_FAILED == 'true' | |
| run: | | |
| echo "❌ npm audit found vulnerabilities. Please run 'npm audit fix' or review manually." | |
| exit 1 |