|
|
@@ -0,0 +1,213 @@
|
|
|
+name: Security Audit
|
|
|
+
|
|
|
+on:
|
|
|
+ schedule:
|
|
|
+ # Run weekly on Monday at 6:00 UTC
|
|
|
+ - cron: '0 6 * * 1'
|
|
|
+ workflow_dispatch:
|
|
|
+ # Allow manual trigger
|
|
|
+
|
|
|
+env:
|
|
|
+ PYTHON_VERSION: '3.11'
|
|
|
+ NODE_VERSION: '20'
|
|
|
+
|
|
|
+jobs:
|
|
|
+ backend-audit:
|
|
|
+ name: Backend Security Audit
|
|
|
+ runs-on: ubuntu-latest
|
|
|
+ steps:
|
|
|
+ - uses: actions/checkout@v4
|
|
|
+
|
|
|
+ - name: Set up Python
|
|
|
+ uses: actions/setup-python@v5
|
|
|
+ with:
|
|
|
+ python-version: ${{ env.PYTHON_VERSION }}
|
|
|
+
|
|
|
+ - name: Install dependencies
|
|
|
+ run: |
|
|
|
+ python -m pip install --upgrade pip
|
|
|
+ pip install -r requirements.txt
|
|
|
+ pip install pip-audit
|
|
|
+
|
|
|
+ - name: Run pip-audit
|
|
|
+ id: pip-audit
|
|
|
+ run: |
|
|
|
+ pip-audit --desc on --format json --output pip-audit-results.json || echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT
|
|
|
+ pip-audit --desc on || true
|
|
|
+
|
|
|
+ - name: Upload audit results
|
|
|
+ if: always()
|
|
|
+ uses: actions/upload-artifact@v4
|
|
|
+ with:
|
|
|
+ name: pip-audit-results
|
|
|
+ path: pip-audit-results.json
|
|
|
+ retention-days: 30
|
|
|
+
|
|
|
+ - name: Create issue on vulnerability
|
|
|
+ if: steps.pip-audit.outputs.vulnerabilities_found == 'true'
|
|
|
+ uses: actions/github-script@v7
|
|
|
+ with:
|
|
|
+ script: |
|
|
|
+ const fs = require('fs');
|
|
|
+ const results = JSON.parse(fs.readFileSync('pip-audit-results.json', 'utf8'));
|
|
|
+
|
|
|
+ // Build vulnerability table
|
|
|
+ let table = '| Package | Version | Vulnerability | Fix Version |\n';
|
|
|
+ table += '|---------|---------|---------------|-------------|\n';
|
|
|
+
|
|
|
+ for (const vuln of results.dependencies || []) {
|
|
|
+ for (const v of vuln.vulns || []) {
|
|
|
+ table += `| ${vuln.name} | ${vuln.version} | ${v.id} | ${v.fix_versions?.join(', ') || 'N/A'} |\n`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const title = `Security Alert: ${results.dependencies?.reduce((acc, d) => acc + (d.vulns?.length || 0), 0) || 0} Python vulnerabilities found`;
|
|
|
+
|
|
|
+ // Check for existing open issue
|
|
|
+ const existingIssues = await github.rest.issues.listForRepo({
|
|
|
+ owner: context.repo.owner,
|
|
|
+ repo: context.repo.repo,
|
|
|
+ state: 'open',
|
|
|
+ labels: 'security,automated'
|
|
|
+ });
|
|
|
+
|
|
|
+ const existingIssue = existingIssues.data.find(i => i.title.startsWith('Security Alert:') && i.title.includes('Python'));
|
|
|
+
|
|
|
+ const body = `## Automated Security Audit Results
|
|
|
+
|
|
|
+ The weekly security audit found vulnerabilities in Python dependencies.
|
|
|
+
|
|
|
+ ${table}
|
|
|
+
|
|
|
+ ### Recommended Actions
|
|
|
+
|
|
|
+ 1. Review each vulnerability
|
|
|
+ 2. Update affected packages: \`pip install --upgrade <package>\`
|
|
|
+ 3. Run \`pip-audit\` locally to verify fixes
|
|
|
+ 4. Close this issue when resolved
|
|
|
+
|
|
|
+ ---
|
|
|
+ *This issue was automatically created by the security audit workflow.*`;
|
|
|
+
|
|
|
+ if (existingIssue) {
|
|
|
+ await github.rest.issues.update({
|
|
|
+ owner: context.repo.owner,
|
|
|
+ repo: context.repo.repo,
|
|
|
+ issue_number: existingIssue.number,
|
|
|
+ body: body
|
|
|
+ });
|
|
|
+ console.log(`Updated existing issue #${existingIssue.number}`);
|
|
|
+ } else {
|
|
|
+ await github.rest.issues.create({
|
|
|
+ owner: context.repo.owner,
|
|
|
+ repo: context.repo.repo,
|
|
|
+ title: title,
|
|
|
+ body: body,
|
|
|
+ labels: ['security', 'automated', 'dependencies']
|
|
|
+ });
|
|
|
+ console.log('Created new security issue');
|
|
|
+ }
|
|
|
+
|
|
|
+ frontend-audit:
|
|
|
+ name: Frontend Security Audit
|
|
|
+ runs-on: ubuntu-latest
|
|
|
+ steps:
|
|
|
+ - uses: actions/checkout@v4
|
|
|
+
|
|
|
+ - name: Set up Node.js
|
|
|
+ uses: actions/setup-node@v4
|
|
|
+ with:
|
|
|
+ node-version: ${{ env.NODE_VERSION }}
|
|
|
+ cache: 'npm'
|
|
|
+ cache-dependency-path: frontend/package-lock.json
|
|
|
+
|
|
|
+ - name: Install dependencies
|
|
|
+ working-directory: frontend
|
|
|
+ run: npm ci
|
|
|
+
|
|
|
+ - name: Run npm audit
|
|
|
+ id: npm-audit
|
|
|
+ working-directory: frontend
|
|
|
+ run: |
|
|
|
+ npm audit --json > npm-audit-results.json || echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT
|
|
|
+ npm audit --audit-level=moderate || true
|
|
|
+
|
|
|
+ - name: Upload audit results
|
|
|
+ if: always()
|
|
|
+ uses: actions/upload-artifact@v4
|
|
|
+ with:
|
|
|
+ name: npm-audit-results
|
|
|
+ path: frontend/npm-audit-results.json
|
|
|
+ retention-days: 30
|
|
|
+
|
|
|
+ - name: Create issue on vulnerability
|
|
|
+ if: steps.npm-audit.outputs.vulnerabilities_found == 'true'
|
|
|
+ uses: actions/github-script@v7
|
|
|
+ with:
|
|
|
+ script: |
|
|
|
+ const fs = require('fs');
|
|
|
+ const results = JSON.parse(fs.readFileSync('frontend/npm-audit-results.json', 'utf8'));
|
|
|
+
|
|
|
+ const vulns = results.vulnerabilities || {};
|
|
|
+ const vulnCount = Object.keys(vulns).length;
|
|
|
+
|
|
|
+ if (vulnCount === 0) {
|
|
|
+ console.log('No vulnerabilities to report');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Build vulnerability table
|
|
|
+ let table = '| Package | Severity | Via | Fix |\n';
|
|
|
+ table += '|---------|----------|-----|-----|\n';
|
|
|
+
|
|
|
+ for (const [name, info] of Object.entries(vulns)) {
|
|
|
+ const via = Array.isArray(info.via) ? info.via.map(v => typeof v === 'string' ? v : v.name).join(', ') : info.via;
|
|
|
+ table += `| ${name} | ${info.severity} | ${via} | ${info.fixAvailable ? 'Yes' : 'No'} |\n`;
|
|
|
+ }
|
|
|
+
|
|
|
+ const title = `Security Alert: ${vulnCount} npm vulnerabilities found`;
|
|
|
+
|
|
|
+ // Check for existing open issue
|
|
|
+ const existingIssues = await github.rest.issues.listForRepo({
|
|
|
+ owner: context.repo.owner,
|
|
|
+ repo: context.repo.repo,
|
|
|
+ state: 'open',
|
|
|
+ labels: 'security,automated'
|
|
|
+ });
|
|
|
+
|
|
|
+ const existingIssue = existingIssues.data.find(i => i.title.startsWith('Security Alert:') && i.title.includes('npm'));
|
|
|
+
|
|
|
+ const body = `## Automated Security Audit Results
|
|
|
+
|
|
|
+ The weekly security audit found vulnerabilities in npm dependencies.
|
|
|
+
|
|
|
+ ${table}
|
|
|
+
|
|
|
+ ### Recommended Actions
|
|
|
+
|
|
|
+ 1. Review each vulnerability: \`npm audit\`
|
|
|
+ 2. Auto-fix if possible: \`npm audit fix\`
|
|
|
+ 3. Manual fix for breaking changes: \`npm audit fix --force\` (review changes!)
|
|
|
+ 4. Close this issue when resolved
|
|
|
+
|
|
|
+ ---
|
|
|
+ *This issue was automatically created by the security audit workflow.*`;
|
|
|
+
|
|
|
+ if (existingIssue) {
|
|
|
+ await github.rest.issues.update({
|
|
|
+ owner: context.repo.owner,
|
|
|
+ repo: context.repo.repo,
|
|
|
+ issue_number: existingIssue.number,
|
|
|
+ body: body
|
|
|
+ });
|
|
|
+ console.log(`Updated existing issue #${existingIssue.number}`);
|
|
|
+ } else {
|
|
|
+ await github.rest.issues.create({
|
|
|
+ owner: context.repo.owner,
|
|
|
+ repo: context.repo.repo,
|
|
|
+ title: title,
|
|
|
+ body: body,
|
|
|
+ labels: ['security', 'automated', 'dependencies']
|
|
|
+ });
|
|
|
+ console.log('Created new security issue');
|
|
|
+ }
|