-
Notifications
You must be signed in to change notification settings - Fork 0
276 lines (265 loc) · 11.1 KB
/
security-scan.yml
File metadata and controls
276 lines (265 loc) · 11.1 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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Copyright 2026 ResQ Software
# SPDX-License-Identifier: Apache-2.0
#
# Reusable security scan — callable from any resq-software/* repo with a
# thin wrapper (see .github/workflows/README.md).
#
# Defense-in-depth layers:
# harden-runner Audit/block runner egress (defense vs hijacked actions)
# codeql SAST per language
# gitleaks Git history secret scan
# osv-scanner Dependency vulnerability scan (Google OSV)
# dependency-review PR-only; blocks high-severity CVEs
# snyk Opt-in; requires SNYK_TOKEN
# zizmor Static audit of workflow YAML for security anti-patterns
# actionlint Workflow YAML correctness lint
# semgrep Opt-in SAST; requires SEMGREP_APP_TOKEN
#
# All third-party `uses:` refs are SHA-pinned with a trailing `# <tag>`
# comment so Dependabot can still propose updates.
name: security-scan
on:
workflow_call:
inputs:
languages:
description: >
JSON array of CodeQL languages (e.g. '["python","javascript-typescript"]').
Pass an empty array '[]' or omit to skip CodeQL.
Valid members: actions, c-cpp, csharp, go, java-kotlin, javascript-typescript, python, ruby, swift.
type: string
required: false
default: "[]"
enable-gitleaks:
description: >
Requires GITLEAKS_LICENSE (even for public org repos). Off by default —
GitHub's native secret scanning + push protection cover public repos.
type: boolean
required: false
default: false
enable-osv:
type: boolean
required: false
default: true
enable-dependency-review:
type: boolean
required: false
default: true
enable-snyk:
description: Requires SNYK_TOKEN repo secret.
type: boolean
required: false
default: false
enable-semgrep:
description: Requires SEMGREP_APP_TOKEN repo secret.
type: boolean
required: false
default: false
enable-zizmor:
type: boolean
required: false
default: true
enable-actionlint:
type: boolean
required: false
default: true
submodules:
description: >
Checkout submodules for the CodeQL job (autobuild may need them).
Pass 'true' for top-level, 'recursive' for nested, or '' to skip.
type: string
required: false
default: ""
permissions:
contents: read
security-events: write # CodeQL + SARIF uploads
pull-requests: read # dependency-review
jobs:
# ── CodeQL ────────────────────────────────────────────────────────────────
# CodeQL is handled by GitHub's repo-level "default setup" (Settings →
# Code security → CodeQL analysis). Running a matrix job here conflicts
# with default setup ("analyses from advanced configurations cannot be
# processed when the default setup is enabled") — so the job is a no-op
# unless `languages` is explicitly set AND the caller has disabled
# default setup in repo settings.
codeql:
name: CodeQL (${{ matrix.language }})
if: ${{ inputs.languages != '' && inputs.languages != '[]' }}
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
# `actions: read` is required by github/codeql-action's telemetry —
# init/analyze each call `GET /repos/{owner}/{repo}/actions/runs/{run_id}`.
# Without the scope every step ends in
# ##[error]Resource not accessible by integration
# and the job fails. Read-only; grants no write capability.
actions: read
strategy:
fail-fast: false
matrix:
language: ${{ fromJSON(inputs.languages) }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
submodules: ${{ inputs.submodules }}
- name: Initialize CodeQL
uses: github/codeql-action/init@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # v3
- name: Analyze
uses: github/codeql-action/analyze@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # v3
with:
category: "/language:${{ matrix.language }}"
continue-on-error: true # default-setup collision otherwise fails
# ── Gitleaks ──────────────────────────────────────────────────────────────
gitleaks:
name: gitleaks
if: ${{ inputs.enable-gitleaks }}
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
# ── OSV-Scanner ───────────────────────────────────────────────────────────
osv-scanner:
name: osv-scanner
if: ${{ inputs.enable-osv }}
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
with:
go-version: "stable" # osv-scanner v2.3.5 needs Go ≥ 1.26.1
- name: Install + run OSV-Scanner
run: |
set -euxo pipefail
go install github.com/google/osv-scanner/v2/cmd/osv-scanner@v2.3.5
# Exit 128 from osv-scanner = "no package sources found"; treat as
# success for scripts-only / manifest-free repos.
"$(go env GOPATH)/bin/osv-scanner" scan source --recursive ./ || {
rc=$?
if [ "$rc" -eq 128 ]; then
echo "osv-scanner: no package sources in this repo — skipping."
exit 0
fi
exit "$rc"
}
# ── Dependency Review (PR only) ───────────────────────────────────────────
dependency-review:
name: dependency-review
if: ${{ inputs.enable-dependency-review && github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4
with:
fail-on-severity: high
comment-summary-in-pr: on-failure
# ── zizmor (workflow-YAML security audit) ─────────────────────────────────
zizmor:
name: zizmor
if: ${{ inputs.enable-zizmor }}
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
# Required by github/codeql-action/upload-sarif's telemetry call
# to `GET /repos/{owner}/{repo}/actions/runs/{run_id}`. Without
# this the upload step ends in
# ##[error]Resource not accessible by integration
# and fails the job. Read-only; grants no write capability.
actions: read
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install zizmor
run: pipx install zizmor
- name: Run zizmor
run: zizmor --format sarif .github/workflows/ > zizmor.sarif
continue-on-error: true
- name: Upload zizmor SARIF
if: always() && hashFiles('zizmor.sarif') != ''
uses: github/codeql-action/upload-sarif@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # v3
with:
sarif_file: zizmor.sarif
category: zizmor
# ── actionlint (workflow-YAML correctness lint) ───────────────────────────
actionlint:
name: actionlint
if: ${{ inputs.enable-actionlint }}
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install + run actionlint
run: |
set -euxo pipefail
bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
./actionlint -color
# ── Semgrep (opt-in SAST) ─────────────────────────────────────────────────
semgrep:
name: semgrep
if: ${{ inputs.enable-semgrep }}
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Run Semgrep CI
run: semgrep ci
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
# ── Snyk (opt-in) ─────────────────────────────────────────────────────────
snyk:
name: snyk
if: ${{ inputs.enable-snyk }}
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
with:
egress-policy: audit
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Run Snyk
uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- name: Upload Snyk SARIF
if: always() && hashFiles('snyk.sarif') != ''
uses: github/codeql-action/upload-sarif@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # v3
with:
sarif_file: snyk.sarif
category: snyk