-
Notifications
You must be signed in to change notification settings - Fork 114
184 lines (159 loc) · 5.85 KB
/
security.yml
File metadata and controls
184 lines (159 loc) · 5.85 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
178
179
180
181
182
183
184
name: Security Scanning
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# Run full scan nightly at 02:00 UTC to catch newly disclosed CVEs
- cron: '0 2 * * *'
workflow_dispatch:
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: '20'
jobs:
# Dependency Scan — npm audit + Snyk SCA
dependency-scan:
name: Dependency Scan (SCA)
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # required to upload SARIF to GitHub Security tab
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm install
# ── npm audit (built-in, zero config) ──────────────────────────────────
- name: npm audit
run: npm audit --audit-level=high
# Fails the job if any HIGH or CRITICAL vulnerabilities are found.
# Change to --audit-level=moderate to tighten, or =critical to loosen.
# ── Snyk SCA (deeper graph analysis, licence checks) ───────────────────
- name: Snyk — dependency vulnerability scan
uses: snyk/actions/node@master
continue-on-error: true # upload results even when vulnerabilities exist
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: >
--severity-threshold=high
--sarif-file-output=snyk-sca.sarif
- name: Upload Snyk SCA results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always() && hashFiles('snyk-sca.sarif') != ''
with:
sarif_file: snyk-sca.sarif
category: snyk-sca
# Code Scanning — GitHub CodeQL SAST
code-scan:
name: Code Scan (SAST — CodeQL)
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: javascript-typescript
# Built-in security-and-quality query suite — covers OWASP Top 10,
# injection, path traversal, broken auth, and more.
queries: security-and-quality
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm install
- name: Build (required for CodeQL to trace the compilation graph)
run: npm run build
- name: Perform CodeQL analysis
uses: github/codeql-action/analyze@v4
with:
category: codeql-typescript
# Container Scanning — Trivy image scan
container-scan:
name: Container Scan (Trivy)
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build \
--target production \
--tag teachlink-backend:${{ github.sha }} \
.
# ── Trivy — OS packages + application dependencies in the image ─────────
- name: Trivy — image vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: teachlink-backend:${{ github.sha }}
format: sarif
output: trivy-image.sarif
severity: HIGH,CRITICAL
exit-code: '1' # fail job on HIGH/CRITICAL findings
ignore-unfixed: true # skip vulnerabilities with no upstream fix yet
- name: Upload Trivy image results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always() && hashFiles('trivy-image.sarif') != ''
uses: github/codeql-action/upload-sarif@v4
if: hashFiles('trivy-image.sarif') != ''
with:
sarif_file: trivy-image.sarif
category: trivy-image
# ── Trivy — IaC / config scan (docker-compose, Dockerfiles) ────────────
- name: Trivy — IaC config scan
uses: aquasecurity/trivy-action@master
with:
scan-type: config
scan-ref: .
format: sarif
output: trivy-iac.sarif
severity: HIGH,CRITICAL
exit-code: '0' # advisory only — don't block on misconfiguration
continue-on-error: true
- name: Upload Trivy IaC results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always() && hashFiles('trivy-iac.sarif') != ''
uses: github/codeql-action/upload-sarif@v4
if: hashFiles('trivy-iac.sarif') != ''
with:
sarif_file: trivy-iac.sarif
category: trivy-iac
# Aggregate gate — mirrors the "CI Passed" pattern from ci.yml
# Branch protection can optionally require "Security Passed" as a status check
security-passed:
name: Security Passed
runs-on: ubuntu-latest
needs: [dependency-scan, code-scan, container-scan]
if: always()
steps:
- name: Check all security jobs passed
env:
NEEDS_JSON: ${{ toJSON(needs) }}
run: |
node -e '
const needs = JSON.parse(process.env.NEEDS_JSON);
const failed = [];
for (const [name, info] of Object.entries(needs)) {
const result = info.result;
console.log(`${name}: ${result}`);
if (result !== "success") failed.push(`${name}=${result}`);
}
if (failed.length) {
console.error(`❌ Security scan failed: ${failed.join(", ")}`);
process.exit(1);
}
console.log("✅ All security scans passed.");
'