-
Notifications
You must be signed in to change notification settings - Fork 1
251 lines (231 loc) · 11.2 KB
/
Copy pathsecurity.yml
File metadata and controls
251 lines (231 loc) · 11.2 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
name: Security
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Weekly scan every Monday at 02:00 UTC
- cron: "0 2 * * 1"
# Supersede in-flight runs for the same PR; see .claude/CLAUDE.md "Superseding CI runs".
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# ── Monorepo Structure Maintenance ────────────────────────────────────────────
# Checks that per-language tooling is fully wired up before per-module jobs run. Covers Go
# (module matrix completeness, .golangci.yml presence) and Python (workspace tool config,
# member glob coverage, requirements_lock.txt freshness). uv is needed for the lock check;
# setup-uv brings it alongside the 3.14 Python interpreter the script targets.
modules-check:
name: Module completeness check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
# renovate: datasource=python-version depName=python
python-version: "3.14"
- run: python3 meta/scripts/check_modules.py
# ── Static Application Security Testing (SAST) ───────────────────────────────
# Analyses source code for security vulnerabilities — injection flaws, insecure API usage,
# hardcoded secrets — from the source rather than from a resolved dependency set. Two engines run
# here: Semgrep below, and CodeQL after it. Semgrep uses per-language rule packs, passed via
# SEMGREP_RULES; add one per adopted language. The deprecated semgrep/semgrep-action is
# replaced by the upstream-recommended semgrep/semgrep container image running `semgrep ci`, so
# the tool version is SHA-pinned like everything else (the action froze at v0.58.0 — see README's
# "Dependency updates").
# TEND(lang-expand): add p/<language> to SEMGREP_RULES for each adopted language (semgrep.dev/r
# lists available packs, e.g. p/java, p/typescript, p/rust).
semgrep:
name: Semgrep
runs-on: ubuntu-latest
container:
image: semgrep/semgrep:1.174.0@sha256:f1f7b71861c7b28b6e0f661225a2c4f58a484f5d0f182465c6d6b3b22f972ade
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: semgrep ci
env:
SEMGREP_RULES: p/golang p/python p/secrets
# CodeQL, the second SAST engine and the one code scanning's own UI reads. Advanced setup on
# purpose — see .claude/CLAUDE.md "CodeQL runs as advanced setup" for what GitHub's managed
# default setup could not do, and for the repo setting that has to stay off for this to run.
codeql:
name: CodeQL Analysis (${{ matrix.language }})
runs-on: ubuntu-latest
permissions:
contents: read
# Uploads the SARIF. The starter workflow's `actions: read` (private repos) and
# `packages: read` (private CodeQL packs) buy nothing here; this repo is public and
# queries come from the default suite.
security-events: write
strategy:
fail-fast: false
matrix:
# TEND(lang-expand): one entry per adopted language. `none` extracts without building and
# is what interpreted languages want; a compiled language has no such option and needs its
# toolchain installed below, the way Go does.
include:
- language: actions
build-mode: none
- language: go
build-mode: autobuild
- language: python
build-mode: none
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Ahead of init on purpose: the Go autobuilder runs under GOTOOLCHAIN=local, so whatever
# is on PATH when extraction starts is the only toolchain it can use.
# //meta/scripts:test_codeql_toolchain keeps this step, and its position, from being lost.
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
if: matrix.language == 'go'
with:
go-version-file: go.work
# Globbed, not listed: this job has no module matrix to key off, and a hand-listed
# module would go stale the day a second one lands.
cache-dependency-path: |
go.work.sum
**/go.sum
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
languages: ${{ matrix.language }}
# No separate autobuild step: that action documents itself as superseded by this input.
build-mode: ${{ matrix.build-mode }}
- uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
# The string default setup uploaded under; it is what separates one language's
# results from another's within a tool.
category: "/language:${{ matrix.language }}"
# Fan-in for the ruleset; the pattern's reasoning is at devcontainer.yml's `base-image-all`.
# Strict on `skipped` unlike that one, since nothing gates this matrix. `if: always()` is
# load-bearing: without it a failed matrix skips this job, and a skipped check counts as
# passing. //meta/scripts:test_codeql_toolchain keeps both.
codeql-all:
name: CodeQL Analysis (all languages)
runs-on: ubuntu-latest
needs: [codeql]
if: always()
steps:
- name: Verify all codeql jobs passed
run: |
if [ "${{ needs.codeql.result }}" != "success" ]; then
echo "codeql result: ${{ needs.codeql.result }}"
exit 1
fi
echo "All codeql jobs passed"
# ── Dependency CVE Scanning ───────────────────────────────────────────────────
# Per-language scanners that flag known-vulnerable third-party dependencies. The signal model
# differs by language — govulncheck does call-graph reachability (only fires when our code
# reaches the vulnerable symbol); pip-audit is manifest-based (fires on any flagged version in
# the resolved set). Both run once per resolution unit: govulncheck per Go module, pip-audit
# once over the workspace-wide uv resolution (uv enforces single-version-per-package across
# workspace members, so one scan covers every member).
# TEND(lang-expand): add an equivalent per-language scanner as languages are adopted
# (e.g. cargo-audit for Rust, trivy fs --scanners vuln for languages without a dedicated
# reachability tool).
govulncheck:
name: govulncheck (${{ matrix.go_module }})
runs-on: ubuntu-latest
needs: [modules-check]
permissions:
contents: read
strategy:
fail-fast: false
matrix:
go_module:
- tools/network_infrastructure_maintenance
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The setup/install/run trio below is what golang/govulncheck-action wraps. We inline it:
# the action is a thin composite, and its own pinned actions/checkout + actions/setup-go
# trail current releases far enough to emit Node runtime deprecation warnings in every
# consuming workflow. If this ever outgrows install-and-run, check whether that action has
# started keeping its dependencies current and switch back.
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
# go.work pins the toolchain so a workspace-level `go X.Y.Z` directive doesn't trip
# GOTOOLCHAIN=local.
go-version-file: go.work
cache-dependency-path: |
go.work.sum
${{ matrix.go_module }}/go.sum
- run: go install golang.org/x/vuln/cmd/govulncheck@latest
- run: govulncheck -C ${{ matrix.go_module }} ./...
govulncheck-all:
name: govulncheck (all modules)
runs-on: ubuntu-latest
needs: [govulncheck]
if: always()
steps:
- name: Verify all govulncheck jobs passed
run: |
if [ "${{ needs.govulncheck.result }}" != "success" ]; then
echo "govulncheck result: ${{ needs.govulncheck.result }}"
exit 1
fi
echo "All govulncheck jobs passed"
# Python sibling of govulncheck. pip-audit reads requirements.txt format; we feed it a
# fresh `uv export` of uv.lock (the source of truth) into a tempfile rather than the
# committed requirements_lock.txt, so this scan is decoupled from whether the derived
# file is currently in sync — pre-commit and the Renovate auto-commit workflow each
# police that on their own paths.
pip-audit:
name: pip-audit
runs-on: ubuntu-latest
needs: [modules-check]
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
# renovate: datasource=python-version depName=python
python-version: "3.14"
- name: Export uv.lock to requirements.txt
run: |
uv export --format requirements-txt --no-emit-project \
--output-file "${RUNNER_TEMP}/requirements.txt"
- name: pip-audit
env:
# renovate: datasource=pypi depName=pip-audit
PIP_AUDIT_VERSION: "2.10.1"
# `--require-hashes` makes pip-audit trust the file as the resolution (implies
# `--no-deps`) AND fail if any line lacks a hash — a regression check against an
# un-hashed export sneaking back in. `--strict` promotes any remaining warning
# class to a failure.
run: uvx "pip-audit@${PIP_AUDIT_VERSION}" --requirement "${RUNNER_TEMP}/requirements.txt" --require-hashes --strict
# ── Supply Chain and Filesystem Scanning ─────────────────────────────────────
# Scans the entire repository for secrets committed to source, dependency CVEs across all
# ecosystems, and license compliance. Language-agnostic — Trivy covers new languages automatically
# as they are added.
trivy:
name: Trivy
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
format: sarif
output: trivy.sarif
- uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
if: always()
with:
sarif_file: trivy.sarif
category: trivy