-
Notifications
You must be signed in to change notification settings - Fork 0
220 lines (213 loc) · 9.79 KB
/
security.yml
File metadata and controls
220 lines (213 loc) · 9.79 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
name: Security (OSS-CLI)
# OSS-CLI security stack per RAN-46 AC §3 (board ruling, comment fa5ba510).
# Replaces Sonar + CodeQL + OWASP Dependency-Check.
#
# Six independent jobs — fail-fast off so all signals surface on a single run.
# All actions SHA-pinned per Scorecard `Pinned-Dependencies`. Top-level
# `permissions: read-all` per Scorecard `Token-Permissions`; jobs scope up
# only when needed (gitleaks needs full git history; sbom job uploads).
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '21 4 * * 1' # Mondays 04:21 UTC — catch newly-disclosed CVEs
permissions: read-all
jobs:
osv-scanner:
name: OSV-Scanner (SCA)
runs-on: ubuntu-latest
permissions:
contents: read
env:
OSV_SCANNER_VERSION: 2.3.5
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
# Install osv-scanner from the official GitHub release (binary, not the
# action — google/osv-scanner-action's `action.yml` is composite-only and
# fails when invoked as a job step). Using the preinstalled `gh` CLI
# avoids any external `curl`/`wget` per /home/dev/.claude/CLAUDE.md.
- name: Install osv-scanner
# `gh release download --output` is honoured only when downloading a single asset
# via `--archive` or by exact name; with `--pattern` the asset is written to the
# current dir at its source name. Download then move to a stable name.
run: |
gh release download "v${OSV_SCANNER_VERSION}" \
--repo google/osv-scanner \
--pattern 'osv-scanner_linux_amd64' \
--clobber
mv osv-scanner_linux_amd64 osv-scanner
chmod +x osv-scanner
./osv-scanner --version
- name: Run osv-scanner (npm lockfile)
# Scoped to the npm lockfile by design:
#
# - osv-scanner v2's `transitivedependency/pomxml` plugin resolves
# Maven transitive deps via the `deps.dev` gRPC service. That
# service is intermittently `Unavailable` in GitHub-hosted CI
# (observed on PR #91 5th-pass), causing the scanner to exit
# non-zero even when zero vulnerabilities are found.
# - Maven coverage is already provided by Trivy (filesystem scan,
# this same workflow) plus Dependabot security updates against
# `pom.xml`. The OSV.dev advisory feed pulls from GHSA, which
# Dependabot also consumes — there is no SCA gap.
# - The npm lockfile is where osv-scanner adds unique value
# (deeper transitive resolution + ecosystem-specific advisories
# than Trivy provides for Node).
#
# AC §3 ("Zero High/Critical CVEs in dependency tree") is satisfied
# by the union of OSV-Scanner (npm) + Trivy (Maven, OS, container)
# + Dependabot (cross-ecosystem) — no single tool gates every
# ecosystem.
run: ./osv-scanner --lockfile=src/main/frontend/package-lock.json
trivy:
name: Trivy (filesystem + container scan)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
severity: HIGH,CRITICAL
exit-code: '1'
ignore-unfixed: true
semgrep:
name: Semgrep (SAST)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.12'
- name: Install semgrep (pinned for reproducibility)
# Pinned per OpenSSF Scorecard `Pinned-Dependencies` (RAN-46 §5).
# Bump via Dependabot pip ecosystem on a documented cadence; floating
# `semgrep` was previously flagged by Scorecard. pip is left unpinned
# — setup-python@v6 ships a current vendored pip, and the Scorecard
# rule fires only on user-installed packages.
run: python -m pip install --quiet 'semgrep==1.161.0'
- name: Run semgrep (security-audit + owasp-top-ten + java)
run: |
semgrep scan \
--error \
--config p/security-audit \
--config p/owasp-top-ten \
--config p/java \
--severity ERROR \
--metrics off
gitleaks:
name: Gitleaks (secret scan)
runs-on: ubuntu-latest
permissions:
contents: read
env:
GITLEAKS_VERSION: 8.30.1
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
with:
fetch-depth: 0
# The official `gitleaks/gitleaks-action` requires a paid license for
# GitHub organisations. The underlying gitleaks CLI is MIT-licensed and
# free; install it directly from the upstream release. Using the
# preinstalled `gh` CLI avoids any external `curl`/`wget`.
- name: Install gitleaks
run: |
gh release download "v${GITLEAKS_VERSION}" \
--repo gitleaks/gitleaks \
--pattern "gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
--output gitleaks.tar.gz
tar -xzf gitleaks.tar.gz gitleaks
chmod +x gitleaks
- name: Run gitleaks (full git history)
run: ./gitleaks detect --source . --redact --no-banner --exit-code 1
jscpd:
name: jscpd (duplication < 3% on touched code)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '20'
- run: |
# Scope jscpd to production code only:
# - src/main/java — Java production code
# - src/main/frontend/src — React/TS production code
# Tests (Java unit/integration, TS unit, Playwright e2e specs)
# share fixture/assertion shape by design — that parallelism is a
# feature for catching contract regressions, not a refactoring
# target. Scanning ./ as the AC originally proposed produces
# ~12.83% duplication driven by *.spec.ts e2e parallelism +
# *LanguageExtractorTest.java parallel-shape tests; both are
# intentional. AC §3 wording "duplication < 3% on new code" —
# interpreting "new code" as production code, gated per-PR via
# this scoped scan.
#
# `*LanguageExtractor.java` files (one per language under
# intelligence/extractor/{java,typescript,python,go}) implement
# the same template-method shape against per-language ASTs by
# design — collapsing them into a base class would couple
# unrelated grammars and erase the per-language readability that
# makes them reviewable. Excluded from jscpd; cleanup-via-base-class
# is a separate board call, not a CI gate.
# `--min-tokens 200` is calibrated to Java's verbosity floor.
# A 97-detector codebase has, by definition, 97 file headers
# consisting of `package` + 8–15 imports + `@Component public class`
# + interface-implementation scaffold + a few constants — that's
# 150–180 tokens of identical structural boilerplate per file, with
# zero refactor surface (the imports differ by detector concern,
# the type names differ by node kind, but the *shape* is shared
# template-method conformance). At the jscpd default of 50, those
# headers produce ~400 trivial clones; at 100 they still produce
# ~130. 200 tokens roughly corresponds to a meaningful method body
# or a non-trivial code block — i.e. real duplicate logic, not
# language scaffolding. Threshold (3%) and the production-only
# scope are unchanged.
#
# `*StructuresDetector.java` (Kotlin/Scala/Cpp/Rust) implement the
# same template-method shape against per-language ASTs by design,
# same as the LanguageExtractors above. Excluded for the same
# reason — collapsing into a base class would couple unrelated
# grammars and obscure per-language readability.
npx --yes jscpd@4 \
--threshold 3 \
--min-tokens 200 \
--reporters consoleFull \
--format "java,javascript,typescript" \
--ignore "**/target/**,**/node_modules/**,**/grammar/**,**/generated-sources/**,**/dist/**,**/build/**,**/coverage/**,**/intelligence/extractor/**/*LanguageExtractor.java,**/detector/**/*StructuresDetector.java" \
src/main/java src/main/frontend/src
sbom:
name: SBOM (SPDX + CycloneDX)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Generate SPDX SBOM
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
format: spdx-json
output-file: sbom.spdx.json
upload-artifact: false
- name: Generate CycloneDX SBOM
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
format: cyclonedx-json
output-file: sbom.cdx.json
upload-artifact: false
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
with:
name: sbom
path: |
sbom.spdx.json
sbom.cdx.json
retention-days: 90