Repository navigation
222 lines (204 loc) · 9.91 KB
/
Copy pathtrivy.yml
File metadata and controls
222 lines (204 loc) · 9.91 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
name: Trivy
# Scans the Docker image for OS-layer and dependency CVEs — the one gap
# gitleaks/CodeQL/Dependabot leave open: Debian base-image packages and
# post-release CVE drift live only in the published image.
#
# Two targets:
# - trivy-published (cron + merges to main + dispatch): the GHCR :latest
# and :remote images users pull today, so a CVE disclosed after a
# release still surfaces — merges also keep the README status badge
# current.
# - trivy-pr (pull_request): an image built from the branch, so a CVE
# entering via Dockerfile/base-image changes surfaces before merge.
# Its job ID is the required-status-check context in the branch
# ruleset — tool-prefixed so it's unambiguous in that flat namespace.
#
# Scope is vulnerability scanning only — secrets are gitleaks' job, and
# the IaC here is Pulumi TypeScript, which Trivy doesn't parse.
#
# trivy-pr gates merges: it exits non-zero on CRITICAL/HIGH findings,
# and is a required status check in the branch ruleset. trivy-published
# stays report-only — its findings of every severity land in the Security
# tab; there is no merge to block.
on:
schedule:
- cron: "23 9 * * 1" # weekly, Monday 09:23 UTC
push:
branches: [main]
pull_request:
workflow_dispatch:
# Top-level token is read-only; each job grants its own SARIF upload.
permissions:
contents: read
jobs:
trivy-published:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # SARIF upload to the Security tab
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The images may not be pullable — a fresh fork, or a repo that
# hasn't cut its first release — and this job also fires on every
# push to main, so gate the scans on the manifests existing rather
# than failing the run (and the README badge).
- name: Check published images exist
id: images
run: |
if docker manifest inspect "ghcr.io/${{ vars.GHCR_USER }}/vault-cortex:latest" >/dev/null 2>&1; then
echo "latest=true" >> "$GITHUB_OUTPUT"
else
echo "latest=false" >> "$GITHUB_OUTPUT"
echo "::notice::vault-cortex:latest not published yet — skipping scan"
fi
if docker manifest inspect "ghcr.io/${{ vars.GHCR_USER }}/vault-cortex:remote" >/dev/null 2>&1; then
echo "remote=true" >> "$GITHUB_OUTPUT"
else
echo "remote=false" >> "$GITHUB_OUTPUT"
echo "::notice::vault-cortex:remote not published yet — skipping scan"
fi
- name: Scan published image (local target)
if: steps.images.outputs.latest == 'true'
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # 0.36.0
with:
# Manifest list resolves per-runner (amd64 here); the Debian OS
# package set is identical across arches, so amd64-only is fine.
image-ref: ghcr.io/${{ vars.GHCR_USER }}/vault-cortex:latest
scanners: vuln
# No severity filter, so every fixable finding reaches the Security
# tab. With SARIF output the action drops the filter anyway unless
# limit-severities-for-sarif is set, which only the trivy-pr gate does.
ignore-unfixed: true
format: sarif
output: trivy-results.sarif
exit-code: "0"
# hashFiles guards: skip the upload when the corresponding scan was
# skipped or died before writing its SARIF, instead of layering a
# missing-file error on top of the real outcome.
- name: Upload SARIF to the Security tab
if: always() && hashFiles('trivy-results.sarif') != ''
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
sarif_file: trivy-results.sarif
category: trivy-published-image
# The :remote target layers s6-overlay + obsidian-headless on top of
# the local target — its extra packages need their own CVE watch.
- name: Scan published image (remote target)
if: steps.images.outputs.remote == 'true'
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # 0.36.0
with:
image-ref: ghcr.io/${{ vars.GHCR_USER }}/vault-cortex:remote
scanners: vuln
# Every severity, as in the local-target scan above.
ignore-unfixed: true
format: sarif
output: trivy-results-remote.sarif
exit-code: "0"
- name: Upload remote SARIF to the Security tab
if: always() && hashFiles('trivy-results-remote.sarif') != ''
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
sarif_file: trivy-results-remote.sarif
category: trivy-published-image-remote
trivy-pr:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # SARIF upload to the Security tab
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
# The base stage's apt-get upgrade layer is content-addressed — it
# caches indefinitely until the Dockerfile or base image changes.
# Passing today's date as APT_UPGRADE_DATE busts that layer daily so
# Debian security patches are always current in the scanned image.
- name: Cache-bust date for apt-get upgrade
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> "$GITHUB_OUTPUT"
# Both Dockerfile targets build and scan as steps of this one job —
# its job ID is the required-status-check context in the branch
# ruleset, so adding a job would need a ruleset update.
- name: Build image from branch (local target)
id: build-local
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
target: local
# Make the built image visible to the local Docker daemon so the
# scan step can reference it by tag.
load: true
tags: vault-cortex:pr-scan
build-args: APT_UPGRADE_DATE=${{ steps.date.outputs.date }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build image from branch (remote target)
id: build-remote
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
target: remote
load: true
tags: vault-cortex:pr-scan-remote
build-args: APT_UPGRADE_DATE=${{ steps.date.outputs.date }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Runs whenever the local image built, even if the remote build then
# failed, so a remote-only build error can't hide local-target findings.
- name: Scan branch image (local target)
if: (success() || failure()) && steps.build-local.outcome == 'success'
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # 0.36.0
with:
image-ref: vault-cortex:pr-scan
scanners: vuln
severity: CRITICAL,HIGH
ignore-unfixed: true
format: sarif
# With SARIF output the action drops the severity filter unless
# this is set, and exit-code 1 then fails the PR on any finding.
# MEDIUM and LOW findings still reach the Security tab through
# trivy-published.
limit-severities-for-sarif: true
output: trivy-results.sarif
exit-code: "1" # gate: fail the PR on CRITICAL/HIGH findings
# Runs even when the local scan gates first (exit-code 1), so a
# vulnerability in the shared base image can't mask remote-only
# findings in the extra packages (s6-overlay, obsidian-headless).
# The build-outcome check skips it when there is no remote image.
- name: Scan branch image (remote target)
if: (success() || failure()) && steps.build-remote.outcome == 'success'
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # 0.36.0
with:
image-ref: vault-cortex:pr-scan-remote
scanners: vuln
severity: CRITICAL,HIGH
ignore-unfixed: true
format: sarif
# Keeps the severity filter for the gate, as in the local scan.
limit-severities-for-sarif: true
output: trivy-results-remote.sarif
exit-code: "1" # gate: fail the PR on CRITICAL/HIGH findings
# Fork PRs don't get security-events write access, so the upload
# would 403 — scan results still show in the job log for forks.
# hashFiles guard: no SARIF exists if the build failed before the scan.
- name: Upload SARIF to the Security tab
if: always() && hashFiles('trivy-results.sarif') != '' && github.event.pull_request.head.repo.full_name == github.repository
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
sarif_file: trivy-results.sarif
category: trivy-published-image
# hashFiles guard: the remote SARIF doesn't exist when the remote image
# was never built (its build failed or was skipped), because the remote
# scan is then skipped too.
- name: Upload remote SARIF to the Security tab
if: always() && hashFiles('trivy-results-remote.sarif') != '' && github.event.pull_request.head.repo.full_name == github.repository
uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
sarif_file: trivy-results-remote.sarif
category: trivy-published-image-remote