security.yml 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. name: Security Audit
  2. on:
  3. schedule:
  4. # Run weekly on Monday at 6:00 UTC
  5. - cron: '0 6 * * 1'
  6. workflow_dispatch:
  7. # Allow manual trigger
  8. env:
  9. PYTHON_VERSION: '3.11'
  10. NODE_VERSION: '20'
  11. jobs:
  12. backend-audit:
  13. name: Backend Security Audit
  14. runs-on: ubuntu-latest
  15. steps:
  16. - uses: actions/checkout@v4
  17. - name: Set up Python
  18. uses: actions/setup-python@v5
  19. with:
  20. python-version: ${{ env.PYTHON_VERSION }}
  21. - name: Install dependencies
  22. run: |
  23. python -m pip install --upgrade pip
  24. pip install -r requirements.txt
  25. pip install pip-audit
  26. - name: Run pip-audit
  27. id: pip-audit
  28. run: |
  29. pip-audit --desc on --format json --output pip-audit-results.json || echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT
  30. pip-audit --desc on || true
  31. - name: Upload audit results
  32. if: always()
  33. uses: actions/upload-artifact@v4
  34. with:
  35. name: pip-audit-results
  36. path: pip-audit-results.json
  37. retention-days: 30
  38. - name: Create issue on vulnerability
  39. if: steps.pip-audit.outputs.vulnerabilities_found == 'true'
  40. uses: actions/github-script@v7
  41. with:
  42. script: |
  43. const fs = require('fs');
  44. const results = JSON.parse(fs.readFileSync('pip-audit-results.json', 'utf8'));
  45. // Build vulnerability table
  46. let table = '| Package | Version | Vulnerability | Fix Version |\n';
  47. table += '|---------|---------|---------------|-------------|\n';
  48. for (const vuln of results.dependencies || []) {
  49. for (const v of vuln.vulns || []) {
  50. table += `| ${vuln.name} | ${vuln.version} | ${v.id} | ${v.fix_versions?.join(', ') || 'N/A'} |\n`;
  51. }
  52. }
  53. const title = `Security Alert: ${results.dependencies?.reduce((acc, d) => acc + (d.vulns?.length || 0), 0) || 0} Python vulnerabilities found`;
  54. // Check for existing open issue
  55. const existingIssues = await github.rest.issues.listForRepo({
  56. owner: context.repo.owner,
  57. repo: context.repo.repo,
  58. state: 'open',
  59. labels: 'security,automated'
  60. });
  61. const existingIssue = existingIssues.data.find(i => i.title.startsWith('Security Alert:') && i.title.includes('Python'));
  62. const body = `## Automated Security Audit Results
  63. The weekly security audit found vulnerabilities in Python dependencies.
  64. ${table}
  65. ### Recommended Actions
  66. 1. Review each vulnerability
  67. 2. Update affected packages: \`pip install --upgrade <package>\`
  68. 3. Run \`pip-audit\` locally to verify fixes
  69. 4. Close this issue when resolved
  70. ---
  71. *This issue was automatically created by the security audit workflow.*`;
  72. if (existingIssue) {
  73. await github.rest.issues.update({
  74. owner: context.repo.owner,
  75. repo: context.repo.repo,
  76. issue_number: existingIssue.number,
  77. body: body
  78. });
  79. console.log(`Updated existing issue #${existingIssue.number}`);
  80. } else {
  81. await github.rest.issues.create({
  82. owner: context.repo.owner,
  83. repo: context.repo.repo,
  84. title: title,
  85. body: body,
  86. labels: ['security', 'automated', 'dependencies']
  87. });
  88. console.log('Created new security issue');
  89. }
  90. frontend-audit:
  91. name: Frontend Security Audit
  92. runs-on: ubuntu-latest
  93. steps:
  94. - uses: actions/checkout@v4
  95. - name: Set up Node.js
  96. uses: actions/setup-node@v4
  97. with:
  98. node-version: ${{ env.NODE_VERSION }}
  99. cache: 'npm'
  100. cache-dependency-path: frontend/package-lock.json
  101. - name: Install dependencies
  102. working-directory: frontend
  103. run: npm ci
  104. - name: Run npm audit
  105. id: npm-audit
  106. working-directory: frontend
  107. run: |
  108. npm audit --json > npm-audit-results.json || echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT
  109. npm audit --audit-level=high || true
  110. - name: Upload audit results
  111. if: always()
  112. uses: actions/upload-artifact@v4
  113. with:
  114. name: npm-audit-results
  115. path: frontend/npm-audit-results.json
  116. retention-days: 30
  117. - name: Create issue on vulnerability
  118. if: steps.npm-audit.outputs.vulnerabilities_found == 'true'
  119. uses: actions/github-script@v7
  120. with:
  121. script: |
  122. const fs = require('fs');
  123. const results = JSON.parse(fs.readFileSync('frontend/npm-audit-results.json', 'utf8'));
  124. const vulns = results.vulnerabilities || {};
  125. const vulnCount = Object.keys(vulns).length;
  126. if (vulnCount === 0) {
  127. console.log('No vulnerabilities to report');
  128. return;
  129. }
  130. // Build vulnerability table
  131. let table = '| Package | Severity | Via | Fix |\n';
  132. table += '|---------|----------|-----|-----|\n';
  133. for (const [name, info] of Object.entries(vulns)) {
  134. const via = Array.isArray(info.via) ? info.via.map(v => typeof v === 'string' ? v : v.name).join(', ') : info.via;
  135. table += `| ${name} | ${info.severity} | ${via} | ${info.fixAvailable ? 'Yes' : 'No'} |\n`;
  136. }
  137. const title = `Security Alert: ${vulnCount} npm vulnerabilities found`;
  138. // Check for existing open issue
  139. const existingIssues = await github.rest.issues.listForRepo({
  140. owner: context.repo.owner,
  141. repo: context.repo.repo,
  142. state: 'open',
  143. labels: 'security,automated'
  144. });
  145. const existingIssue = existingIssues.data.find(i => i.title.startsWith('Security Alert:') && i.title.includes('npm'));
  146. const body = `## Automated Security Audit Results
  147. The weekly security audit found vulnerabilities in npm dependencies.
  148. ${table}
  149. ### Recommended Actions
  150. 1. Review each vulnerability: \`npm audit\`
  151. 2. Auto-fix if possible: \`npm audit fix\`
  152. 3. Manual fix for breaking changes: \`npm audit fix --force\` (review changes!)
  153. 4. Close this issue when resolved
  154. ---
  155. *This issue was automatically created by the security audit workflow.*`;
  156. if (existingIssue) {
  157. await github.rest.issues.update({
  158. owner: context.repo.owner,
  159. repo: context.repo.repo,
  160. issue_number: existingIssue.number,
  161. body: body
  162. });
  163. console.log(`Updated existing issue #${existingIssue.number}`);
  164. } else {
  165. await github.rest.issues.create({
  166. owner: context.repo.owner,
  167. repo: context.repo.repo,
  168. title: title,
  169. body: body,
  170. labels: ['security', 'automated', 'dependencies']
  171. });
  172. console.log('Created new security issue');
  173. }