security.yml 7.4 KB

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