forked from AndyMik90/Aperant
-
Notifications
You must be signed in to change notification settings - Fork 0
177 lines (151 loc) · 5.81 KB
/
Copy pathquality-security.yml
File metadata and controls
177 lines (151 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
name: Quality Security
# CodeQL runs on all PRs, pushes to main, and weekly schedule
# Note: CodeQL takes 20-30 min per language (40-60 min total)
# Bandit is fast (5-10 min)
on:
push:
branches: [main]
paths:
- 'apps/**'
- 'tests/**'
- 'pyproject.toml'
- 'package.json'
- '.github/workflows/quality-security.yml'
pull_request:
branches: [main, develop]
paths:
- 'apps/**'
- 'tests/**'
- 'pyproject.toml'
- 'package.json'
- '.github/workflows/quality-security.yml'
schedule:
- cron: '0 0 * * 1' # Weekly on Monday at midnight UTC
concurrency:
group: security-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
security-events: write
actions: read
jobs:
codeql:
name: CodeQL (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
language: [python, javascript-typescript]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: +security-extended,security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
# Bandit runs on all PRs - it's fast (5-10 min)
python-security:
name: Python Security (Bandit)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install Bandit
run: pip install bandit
- name: Run Bandit security scan
id: bandit
run: |
echo "::group::Running Bandit security scan"
bandit -r apps/backend/ -ll -ii -f json -o bandit-report.json || BANDIT_EXIT=$?
if [ "${BANDIT_EXIT:-0}" -gt 1 ]; then
echo "::error::Bandit scan failed with exit code $BANDIT_EXIT"
exit 1
fi
echo "::endgroup::"
- name: Analyze Bandit results
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
if (!fs.existsSync('bandit-report.json')) {
core.setFailed('Bandit report not found - scan may have failed');
return;
}
const report = JSON.parse(fs.readFileSync('bandit-report.json', 'utf8'));
const results = report.results || [];
const high = results.filter(r => r.issue_severity === 'HIGH');
const medium = results.filter(r => r.issue_severity === 'MEDIUM');
const low = results.filter(r => r.issue_severity === 'LOW');
console.log(`::group::Bandit Security Scan Results`);
console.log(`Found ${results.length} issues:`);
console.log(` HIGH: ${high.length}`);
console.log(` MEDIUM: ${medium.length}`);
console.log(` LOW: ${low.length}`);
console.log('::endgroup::');
let summary = `## Python Security Scan (Bandit)\n\n`;
summary += `| Severity | Count |\n`;
summary += `|----------|-------|\n`;
summary += `| High | ${high.length} |\n`;
summary += `| Medium | ${medium.length} |\n`;
summary += `| Low | ${low.length} |\n\n`;
if (high.length > 0) {
summary += `### High Severity Issues\n\n`;
for (const issue of high) {
summary += `- **${issue.filename}:${issue.line_number}**\n`;
summary += ` - ${issue.issue_text}\n`;
summary += ` - Test: \`${issue.test_id}\` (${issue.test_name})\n\n`;
}
}
core.summary.addRaw(summary);
await core.summary.write();
if (high.length > 0) {
core.setFailed(`Found ${high.length} high severity security issue(s)`);
} else {
console.log('No high severity security issues found');
}
# --------------------------------------------------------------------------
# Gate Job - Single check for branch protection
# --------------------------------------------------------------------------
security-summary:
name: Security Summary
runs-on: ubuntu-latest
needs: [codeql, python-security]
if: always()
timeout-minutes: 5
steps:
- name: Check security results
uses: actions/github-script@v8
with:
script: |
const codeql = '${{ needs.codeql.result }}';
const bandit = '${{ needs.python-security.result }}';
console.log('Security Check Results:');
console.log(` CodeQL: ${codeql}`);
console.log(` Bandit: ${bandit}`);
// Only 'failure' is a real failure; 'skipped' is acceptable (e.g., path filters, PR skipping CodeQL)
const acceptable = ['success', 'skipped'];
const codeqlOk = acceptable.includes(codeql);
const banditOk = acceptable.includes(bandit);
const allPassed = codeqlOk && banditOk;
if (allPassed) {
console.log('\n✅ All security checks passed');
core.summary.addRaw('## ✅ Security Checks Passed\n\nAll security scans completed successfully.');
} else {
console.log('\n❌ Some security checks failed');
core.summary.addRaw('## ❌ Security Checks Failed\n\nOne or more security scans found issues.');
core.setFailed('Security checks failed');
}
await core.summary.write();