Security Audit #135
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 Audit | |
| on: | |
| schedule: | |
| # minute hour day-of-month month day-of-week | |
| # Run weekly on Sundays at 2 AM UTC | |
| - cron: '0 2 * * 0' | |
| workflow_dispatch: | |
| push: | |
| branches: [ main ] | |
| paths: | |
| - 'package.json' | |
| - 'package-lock.json' | |
| - '.github/workflows/security-audit.yml' | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| statuses: write | |
| jobs: | |
| security-audit: | |
| name: Security Audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run npm audit | |
| id: audit | |
| run: | | |
| echo "Running npm audit..." | |
| # Try to fix vulnerabilities first | |
| npm audit fix || echo "Some vulnerabilities require manual intervention" | |
| # Run audit and capture results | |
| npm audit --audit-level=moderate --json > audit-results.json || true | |
| # Check if there are any vulnerabilities | |
| VULNERABILITIES=$(cat audit-results.json | jq '.metadata.vulnerabilities.total // 0') | |
| echo "vulnerabilities=$VULNERABILITIES" >> $GITHUB_OUTPUT | |
| if [ "$VULNERABILITIES" -gt 0 ]; then | |
| echo "Found $VULNERABILITIES vulnerabilities" | |
| # Check if vulnerabilities are only in non-critical packages | |
| HIGH_CRITICAL=$(cat audit-results.json | jq '.metadata.vulnerabilities.high + .metadata.vulnerabilities.critical') | |
| if [ "$HIGH_CRITICAL" -gt 0 ]; then | |
| echo "❌ High or critical vulnerabilities found - requires immediate attention" | |
| npm audit --audit-level=moderate | |
| exit 1 | |
| else | |
| echo "⚠️ Only moderate vulnerabilities found" | |
| # Check if vulnerabilities are in acceptable packages | |
| ACCEPTABLE_PACKAGES="react-syntax-highlighter|prismjs|refractor|highlight.js|lowlight" | |
| # Always allow moderate vulnerabilities with proper logging | |
| echo "✅ Moderate vulnerabilities detected - logging for review" | |
| echo "📊 Risk Assessment: Acceptable for deployment" | |
| # Log vulnerability details for monitoring | |
| echo "📋 Vulnerability Details:" | |
| npm audit --audit-level=moderate || echo "Moderate vulnerabilities logged for monitoring" | |
| # Check if vulnerabilities are in acceptable packages | |
| if npm audit --json | jq -r '.vulnerabilities | keys[]' | grep -E "^($ACCEPTABLE_PACKAGES)$" > /dev/null; then | |
| echo "✅ Vulnerabilities confirmed in non-critical packages (code highlighting)" | |
| else | |
| echo "⚠️ Some vulnerabilities in other packages - monitoring required" | |
| fi | |
| fi | |
| else | |
| echo "No vulnerabilities found" | |
| fi | |
| - name: Check for sensitive files | |
| run: | | |
| echo "Checking for sensitive files in repository..." | |
| # Check for common sensitive file patterns | |
| SENSITIVE_FILES=$(find . -type f \( \ | |
| -name "*.env" -o \ | |
| -name "*.key" -o \ | |
| -name "*.pem" -o \ | |
| -name "*.p12" -o \ | |
| -name "*.jks" -o \ | |
| -name "*.keystore" -o \ | |
| -name "*secret*" -o \ | |
| -name "*credential*" -o \ | |
| -name "*password*" -o \ | |
| -name "*.pfx" \ | |
| \) ! -path "./node_modules/*" ! -name ".env.example" ! -name "*.md") | |
| if [ -n "$SENSITIVE_FILES" ]; then | |
| echo "❌ Sensitive files found:" | |
| echo "$SENSITIVE_FILES" | |
| exit 1 | |
| else | |
| echo "✅ No sensitive files found" | |
| fi | |
| - name: Check environment variable configuration | |
| run: | | |
| echo "Verifying environment variable setup..." | |
| # Check if .env.example exists | |
| if [ ! -f ".env.example" ]; then | |
| echo "❌ .env.example file missing" | |
| exit 1 | |
| fi | |
| # Check if .env.local is properly ignored | |
| if [ -f ".env.local" ] && git check-ignore .env.local; then | |
| echo "✅ .env.local is properly ignored" | |
| elif [ -f ".env.local" ]; then | |
| echo "❌ .env.local exists but is not ignored by git" | |
| exit 1 | |
| else | |
| echo "✅ No .env.local file found" | |
| fi | |
| - name: Scan for hardcoded secrets | |
| run: | | |
| echo "Scanning for potential hardcoded secrets..." | |
| # Look for potential API keys, tokens, and secrets in code | |
| POTENTIAL_SECRETS=$(grep -r -i \ | |
| --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \ | |
| --exclude-dir=node_modules --exclude-dir=.next --exclude-dir=.git \ | |
| -E "(api[_-]?key|secret|token|password|credential)" . | \ | |
| grep -v "process.env" | \ | |
| grep -v "// " | \ | |
| grep -v "* " | \ | |
| head -10) | |
| if [ -n "$POTENTIAL_SECRETS" ]; then | |
| echo "⚠️ Potential hardcoded secrets found (review manually):" | |
| echo "$POTENTIAL_SECRETS" | |
| else | |
| echo "✅ No obvious hardcoded secrets found" | |
| fi | |
| - name: Check dependency licenses | |
| run: | | |
| echo "Checking dependency licenses..." | |
| npx license-checker --summary || echo "License checker not available, skipping..." | |
| - name: Create security report | |
| if: steps.audit.outputs.vulnerabilities > 0 | |
| run: | | |
| echo "Creating security report..." | |
| cat > security-report.md << EOF | |
| # Security Audit Report | |
| **Date:** $(date) | |
| **Commit:** ${{ github.sha }} | |
| **Vulnerabilities Found:** ${{ steps.audit.outputs.vulnerabilities }} | |
| ## Audit Results | |
| \`\`\`json | |
| $(cat audit-results.json | jq '.metadata') | |
| \`\`\` | |
| ## Recommendations | |
| 1. Run \`npm audit fix\` to automatically fix vulnerabilities | |
| 2. Review and update dependencies manually if needed | |
| 3. Consider using \`npm audit fix --force\` for breaking changes | |
| 4. Monitor security advisories for used packages | |
| ## Next Steps | |
| - [ ] Review vulnerability details | |
| - [ ] Apply security fixes | |
| - [ ] Test application after fixes | |
| - [ ] Update dependencies to latest secure versions | |
| EOF | |
| - name: Upload security report | |
| if: steps.audit.outputs.vulnerabilities > 0 | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-report | |
| path: | | |
| security-report.md | |
| audit-results.json | |
| retention-days: 30 | |
| - name: Create issue for vulnerabilities | |
| if: steps.audit.outputs.vulnerabilities > 0 && github.event_name == 'schedule' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const vulnerabilities = ${{ steps.audit.outputs.vulnerabilities }}; | |
| // Check if there's already an open security issue | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'security,vulnerability', | |
| state: 'open' | |
| }); | |
| if (issues.data.length === 0) { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `🔒 Security Vulnerabilities Detected (${vulnerabilities} found)`, | |
| body: `**Security audit found ${vulnerabilities} vulnerabilities** | |
| **Detected on:** ${new Date().toISOString()} | |
| **Commit:** ${context.sha} | |
| ## Action Required | |
| 1. Review the security audit results | |
| 2. Run \`npm audit\` locally to see details | |
| 3. Apply fixes using \`npm audit fix\` | |
| 4. Test the application after applying fixes | |
| 5. Update this issue with resolution status | |
| ## Audit Command | |
| \`\`\`bash | |
| npm audit | |
| npm audit fix | |
| \`\`\` | |
| ## Resources | |
| - [npm audit documentation](https://docs.npmjs.com/cli/v8/commands/npm-audit) | |
| - [GitHub Security Advisories](https://github.com/advisories) | |
| - [Node.js Security Working Group](https://github.com/nodejs/security-wg) | |
| **This issue was automatically created by the security audit workflow.**`, | |
| labels: ['security', 'vulnerability', 'automated'] | |
| }); | |
| } | |
| - name: Update commit status | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const vulnerabilities = ${{ steps.audit.outputs.vulnerabilities || 0 }}; | |
| // Read audit results to check severity levels | |
| const fs = require('fs'); | |
| let highCriticalCount = 0; | |
| let moderateCount = 0; | |
| try { | |
| const auditData = JSON.parse(fs.readFileSync('audit-results.json', 'utf8')); | |
| highCriticalCount = (auditData.metadata?.vulnerabilities?.high || 0) + (auditData.metadata?.vulnerabilities?.critical || 0); | |
| moderateCount = auditData.metadata?.vulnerabilities?.moderate || 0; | |
| } catch (error) { | |
| console.log('Could not read audit results, assuming no vulnerabilities'); | |
| } | |
| // Only fail on high/critical vulnerabilities | |
| const state = highCriticalCount > 0 ? 'failure' : 'success'; | |
| const description = highCriticalCount > 0 | |
| ? `${highCriticalCount} high/critical vulnerabilities found` | |
| : moderateCount > 0 | |
| ? `${moderateCount} moderate vulnerabilities (acceptable)` | |
| : 'No significant vulnerabilities detected'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: context.sha, | |
| state: state, | |
| target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, | |
| description: description, | |
| context: 'security-audit' | |
| }); |