Skip to content

Security Scan

Security Scan #626

Workflow file for this run

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