-
Notifications
You must be signed in to change notification settings - Fork 0
187 lines (180 loc) · 7.18 KB
/
security.yml
File metadata and controls
187 lines (180 loc) · 7.18 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
name: Security (OSS-CLI)
# OSS-CLI security stack per RAN-53 AC #5 (mirrors codeiq RAN-46 path B).
# Replaces CodeQL + OWASP Dependency-Check. SonarCloud was originally
# replaced too but was reinstated as a required external gate on
# 2026-04-28 (board reversal); it runs via the SonarCloud GitHub App,
# not as a job in this workflow.
#
# 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
# Workflow-level default: minimum needed for actions/checkout. Each job
# declares the additional narrow scopes (security-events: write for SARIF
# uploaders, contents: write for SBOM artifact upload) it actually needs.
permissions:
contents: read
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`.
- name: Install osv-scanner
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 (Go module + npm lockfile)
# Go's `gomod` lockfile parser in osv-scanner is stable (unlike v2's
# `pomxml` plugin which depends on the deps.dev gRPC service). Both
# ecosystems are covered here directly.
#
# The repo-root `package-lock.json` is an empty stub (no root
# package.json exists) and is intentionally excluded — scanning it
# only adds noise without coverage.
#
# AC §5 ("Zero High/Critical CVEs in dependency tree") is satisfied
# by the union of OSV-Scanner (Go + ui/ npm) + Trivy (filesystem, OS) +
# Dependabot (cross-ecosystem advisories on the Security tab).
run: |
./osv-scanner \
--lockfile=go.mod \
--lockfile=ui/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
run: python -m pip install --quiet --upgrade pip semgrep
- name: Run semgrep (security-audit + owasp-top-ten + golang)
run: |
semgrep scan \
--error \
--config p/security-audit \
--config p/owasp-top-ten \
--config p/golang \
--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.
- 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:
# - internal/ — Go production code
# - ui/src/ — React/TS production code
# Tests, generated code, vendored deps, build artifacts, and the
# legacy internal/graph/ package (CLAUDE.md: "use internal/graphrag/
# for all new graph work") are excluded — they would otherwise
# surface intentional shape-parallelism rather than refactoring
# opportunities.
#
# `--min-tokens 100` is a sensible Go floor: a meaningful Go
# function body is ~40-80 tokens; 100 corresponds to a real
# method body or a non-trivial code block, not import/struct
# boilerplate that 200+ Go files share by convention.
npx --yes jscpd@4 \
--threshold 3 \
--min-tokens 100 \
--reporters consoleFull \
--format "go,javascript,typescript,tsx" \
--ignore "**/vendor/**,**/node_modules/**,**/dist/**,**/build/**,**/coverage/**,**/internal/graph/**,**/*_test.go,**/*.test.ts,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx,**/internal/ui/embed_*.go" \
internal ui/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