name: Security Audit on: schedule: # Run weekly on Monday at 6:00 UTC - cron: '0 6 * * 1' push: paths: - 'backend/**' - 'frontend/**' - 'Dockerfile' - 'docker-compose*.yml' - 'requirements.txt' - 'frontend/package*.json' pull_request: paths: - 'backend/**' - 'frontend/**' - 'Dockerfile' - 'docker-compose*.yml' - 'requirements.txt' - 'frontend/package*.json' workflow_dispatch: # Allow manual trigger env: PYTHON_VERSION: '3.11' NODE_VERSION: '20' # Default permissions for all jobs permissions: contents: read jobs: bandit: name: Python Security Analysis (Bandit) runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install Bandit run: pip install bandit[sarif] - name: Run Bandit run: | bandit -r backend/ -f sarif -o bandit-results.sarif --severity-level medium || true - name: Upload Bandit results to GitHub Security uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: bandit-results.sarif category: bandit trivy: name: Container Security Scan (Trivy) runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - uses: actions/checkout@v4 - name: Build Docker image run: docker build -t bambuddy:security-scan . - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: image-ref: 'bambuddy:security-scan' format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL,HIGH,MEDIUM' - name: Upload Trivy results to GitHub Security uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: trivy-results.sarif category: trivy - name: Run Trivy for Dockerfile/IaC uses: aquasecurity/trivy-action@master with: scan-type: 'config' scan-ref: '.' format: 'sarif' output: 'trivy-config-results.sarif' severity: 'CRITICAL,HIGH,MEDIUM' - name: Upload Trivy config results uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: trivy-config-results.sarif category: trivy-config backend-audit: name: Backend Security Audit runs-on: ubuntu-latest permissions: contents: read issues: write 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 \` 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 permissions: contents: read issues: write 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=high || 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'); }