Daily Security Audit #166
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: Daily Security Audit | |
| on: | |
| schedule: | |
| # Runs at midnight IST (6:30 PM UTC previous day) | |
| - cron: "30 18 * * *" | |
| workflow_dispatch: # Allows manual triggering | |
| jobs: | |
| security-audit: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: packages/browseros-agent | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: packages/browseros-agent/package.json | |
| - name: Install dependencies | |
| run: bun ci | |
| - name: Run security audit | |
| id: audit | |
| continue-on-error: true | |
| run: | | |
| # Run audit and capture output (skip the version line) | |
| bun audit --json 2>&1 | tail -n 1 > audit-results.json || true | |
| # Check if vulnerabilities exist | |
| VULN_COUNT=$(cat audit-results.json | bun -e "const data = JSON.parse(require('fs').readFileSync(0, 'utf-8')); console.log(Object.keys(data).reduce((sum, pkg) => sum + data[pkg].length, 0))") | |
| echo "vuln_count=$VULN_COUNT" >> "$GITHUB_OUTPUT" | |
| - name: Parse audit results | |
| id: parse | |
| if: always() | |
| run: | | |
| cat > parse-audit.ts << 'EOF' | |
| const fs = require('fs'); | |
| const auditData = JSON.parse(fs.readFileSync('audit-results.json', 'utf-8')); | |
| // Collect all vulnerabilities from all packages | |
| const allVulns: any[] = []; | |
| let totalCount = 0; | |
| for (const [packageName, vulns] of Object.entries(auditData)) { | |
| if (Array.isArray(vulns)) { | |
| vulns.forEach((vuln: any) => { | |
| allVulns.push({ ...vuln, packageName }); | |
| totalCount++; | |
| }); | |
| } | |
| } | |
| if (totalCount === 0) { | |
| console.log(JSON.stringify({ | |
| text: "✅ *Daily Security Audit - No Vulnerabilities Found*", | |
| blocks: [ | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: "✅ *Daily Security Audit*\n\nNo vulnerabilities found in dependencies!" | |
| } | |
| }, | |
| { | |
| type: "context", | |
| elements: [ | |
| { | |
| type: "mrkdwn", | |
| text: `Repository: ${process.env.GITHUB_REPOSITORY} | Branch: ${process.env.GITHUB_REF_NAME}` | |
| } | |
| ] | |
| } | |
| ] | |
| })); | |
| process.exit(0); | |
| } | |
| // Count by severity | |
| const severityCounts = { | |
| critical: 0, | |
| high: 0, | |
| moderate: 0, | |
| low: 0 | |
| }; | |
| allVulns.forEach(vuln => { | |
| severityCounts[vuln.severity as keyof typeof severityCounts]++; | |
| }); | |
| let message = `⚠️ *Daily Security Audit - ${totalCount} Vulnerabilit${totalCount === 1 ? 'y' : 'ies'} Found*\n\n`; | |
| message += `*Severity Breakdown:*\n`; | |
| message += `• Critical: ${severityCounts.critical}\n`; | |
| message += `• High: ${severityCounts.high}\n`; | |
| message += `• Moderate: ${severityCounts.moderate}\n`; | |
| message += `• Low: ${severityCounts.low}\n\n`; | |
| message += `*Top Vulnerabilities:*\n`; | |
| // Sort by severity | |
| const severityOrder = { critical: 0, high: 1, moderate: 2, low: 3 }; | |
| allVulns.sort((a, b) => | |
| severityOrder[a.severity as keyof typeof severityOrder] - | |
| severityOrder[b.severity as keyof typeof severityOrder] | |
| ); | |
| allVulns.slice(0, 5).forEach(vuln => { | |
| const emoji = { | |
| critical: '🔴', | |
| high: '🟠', | |
| moderate: '🟡', | |
| low: '🟢' | |
| }[vuln.severity] || '⚪'; | |
| message += `\n${emoji} *${vuln.title}*\n`; | |
| message += ` Package: \`${vuln.packageName}\`\n`; | |
| message += ` Severity: ${vuln.severity.toUpperCase()}\n`; | |
| message += ` Vulnerable: ${vuln.vulnerable_versions}\n`; | |
| if (vuln.cwe?.length) { | |
| message += ` CWE: ${vuln.cwe.join(', ')}\n`; | |
| } | |
| if (vuln.cvss?.score) { | |
| message += ` CVSS: ${vuln.cvss.score}\n`; | |
| } | |
| if (vuln.url) { | |
| message += ` <${vuln.url}|View Details>\n`; | |
| } | |
| }); | |
| if (allVulns.length > 5) { | |
| message += `\n_...and ${allVulns.length - 5} more vulnerabilit${allVulns.length - 5 === 1 ? 'y' : 'ies'}_`; | |
| } | |
| const payload = { | |
| text: `⚠️ Security Audit: ${totalCount} vulnerabilit${totalCount === 1 ? 'y' : 'ies'} found`, | |
| blocks: [ | |
| { | |
| type: "section", | |
| text: { | |
| type: "mrkdwn", | |
| text: message | |
| } | |
| }, | |
| { | |
| type: "actions", | |
| elements: [ | |
| { | |
| type: "button", | |
| text: { | |
| type: "plain_text", | |
| text: "View Full Report" | |
| }, | |
| url: `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}` | |
| } | |
| ] | |
| }, | |
| { | |
| type: "context", | |
| elements: [ | |
| { | |
| type: "mrkdwn", | |
| text: `Repository: ${process.env.GITHUB_REPOSITORY} | Branch: ${process.env.GITHUB_REF_NAME}` | |
| } | |
| ] | |
| } | |
| ] | |
| }; | |
| console.log(JSON.stringify(payload)); | |
| EOF | |
| bun run parse-audit.ts > slack-payload.json | |
| - name: Send to Slack | |
| if: always() | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| run: | | |
| curl -X POST \ | |
| -H 'Content-Type: application/json' \ | |
| -d @slack-payload.json \ | |
| "$SLACK_WEBHOOK_URL" |