Skip to content

Commit f36d6df

Browse files
authored
add grype scan action. enable for 1 scan job in alpha build for testing (#5267)
* add grype scan action. enable for 1 scan job in alpha build for testing * fix syntax and many things. add the grype default config file * move the remaining scan jobs to use the new action * modifications to the jq enrichment to maintain naming format consistency across repos * make this use the action instead of the old reusable workflow * handle case if we can't access the image. ex when we have cut a release, but its not on dockerhub yet * remove old scan job * add additional images to scan * use dotenv to get the right image tags
1 parent e83185d commit f36d6df

File tree

5 files changed

+436
-267
lines changed

5 files changed

+436
-267
lines changed

.github/actions/scan-image/action.yml

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
name: Scan Container Image Grype SARIF
2+
description: 'Scan a container image for vulnerabilities and optionally upload the results for GitHub code scanning'
3+
inputs:
4+
image-ref:
5+
description: 'The image to scan'
6+
required: true
7+
upload-sarif:
8+
description: 'Whether to upload the scan results as a SARIF file'
9+
required: false
10+
default: 'true'
11+
severity-cutoff:
12+
description: 'Minimum severity to report (critical, high, medium, low, negligible)'
13+
required: false
14+
default: 'medium'
15+
fail-build:
16+
description: 'Fail the workflow if vulnerabilities are found'
17+
required: false
18+
default: 'true'
19+
output-file:
20+
description: 'Output file name for SARIF results'
21+
required: false
22+
default: 'results.sarif'
23+
timeout-minutes:
24+
description: 'Maximum time in minutes to wait for the scan to complete'
25+
required: false
26+
default: '30'
27+
retention-days:
28+
description: 'Number of days to retain the scan results artifact'
29+
required: false
30+
default: '90'
31+
category-prefix:
32+
description: 'Prefix to use for the SARIF category name'
33+
required: false
34+
default: 'image-scan-'
35+
only-fixed:
36+
description: 'Only report vulnerabilities that have a fix available'
37+
required: false
38+
default: 'true'
39+
40+
runs:
41+
using: 'composite'
42+
steps:
43+
- name: Extract image details
44+
id: image_details
45+
shell: bash
46+
run: |
47+
IMAGE_NAME=$(echo "${{ inputs.image-ref }}" | cut -d':' -f1)
48+
IMAGE_TAG=$(echo "${{ inputs.image-ref }}" | cut -d':' -f2)
49+
[[ "$IMAGE_TAG" == "$IMAGE_NAME" ]] && IMAGE_TAG="latest"
50+
SAFE_NAME=$(echo "${IMAGE_NAME}-${IMAGE_TAG}" | sed 's/[\/:]/-/g')
51+
{
52+
echo "image_name=${IMAGE_NAME}"
53+
echo "image_tag=${IMAGE_TAG}"
54+
echo "safe_name=${SAFE_NAME}"
55+
} >> "$GITHUB_OUTPUT"
56+
57+
- name: Scan image with Grype
58+
uses: anchore/scan-action@v6
59+
id: scan
60+
continue-on-error: ${{ inputs.fail-build != 'true' }}
61+
with:
62+
image: "${{ inputs.image-ref }}"
63+
fail-build: "${{ inputs.fail-build }}"
64+
severity-cutoff: "${{ inputs.severity-cutoff }}"
65+
output-format: sarif
66+
output-file: "${{ inputs.output-file }}"
67+
by-cve: true
68+
only-fixed: "${{ inputs.only-fixed }}"
69+
70+
- name: Check scan status
71+
if: steps.scan.outcome == 'failure' && inputs.fail-build == 'true'
72+
shell: bash
73+
run: |
74+
echo "::error::Scan failed for image ${{ inputs.image-ref }}"
75+
echo "Please check the scan logs above for details"
76+
exit 1
77+
78+
- name: Enrich or generate SARIF
79+
if: ${{ !cancelled() && inputs.upload-sarif == 'true' }}
80+
shell: bash
81+
run: |
82+
if [ ! -f ${{ inputs.output-file }} ]; then
83+
echo "No SARIF file found — creating minimal empty SARIF"
84+
echo '{"version":"2.1.0","runs":[{"tool":{"driver":{"name":"Anchore Grype","informationUri":"https://github.com/anchore/grype","rules":[]}},"results":[],"properties":{"isFallbackSarif":true}}]}' > ${{ inputs.output-file }}
85+
fi
86+
87+
# Validate SARIF file before enrichment
88+
if ! jq empty ${{ inputs.output-file }}; then
89+
echo "::error::Invalid SARIF file detected"
90+
exit 1
91+
fi
92+
93+
# Create a backup of the original file
94+
cp ${{ inputs.output-file }} ${{ inputs.output-file }}.bak
95+
96+
# Attempt to enrich the SARIF file
97+
if ! jq --arg imageRef "${{ inputs.image-ref }}" \
98+
--arg repo "replicatedhq/kots" \
99+
--arg name "${{ steps.image_details.outputs.image_name }}" \
100+
--arg tag "${{ steps.image_details.outputs.image_tag }}" \
101+
--arg scanTime "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
102+
'
103+
def strip_registry:
104+
if startswith("docker.io/") then
105+
sub("^docker\\.io/"; "")
106+
else
107+
.
108+
end;
109+
def make_asset_id:
110+
"image:" + (. | strip_registry);
111+
.runs[0].properties = {
112+
"imageRef": ($imageRef | strip_registry),
113+
"repository": $repo,
114+
"scanTime": $scanTime,
115+
"DisplayName": ($name + ":" + $tag | make_asset_id),
116+
"UniqueID": ($name + ":" + $tag | make_asset_id),
117+
"imageMetadata": {
118+
"name": ($name | strip_registry),
119+
"tag": $tag,
120+
"annotations": {
121+
"scanTime": $scanTime,
122+
"tool": "grype",
123+
"toolVersion": "latest"
124+
}
125+
}
126+
}' ${{ inputs.output-file }} > enriched-results.sarif; then
127+
echo "::error::Failed to enrich SARIF file"
128+
# Restore the backup
129+
mv ${{ inputs.output-file }}.bak ${{ inputs.output-file }}
130+
exit 1
131+
fi
132+
133+
# Validate the enriched file
134+
if ! jq empty enriched-results.sarif; then
135+
echo "::error::Invalid enriched SARIF file"
136+
# Restore the backup
137+
mv ${{ inputs.output-file }}.bak ${{ inputs.output-file }}
138+
exit 1
139+
fi
140+
141+
mv enriched-results.sarif ${{ inputs.output-file }}
142+
rm -f ${{ inputs.output-file }}.bak
143+
144+
- name: Upload SARIF file
145+
if: ${{ !cancelled() && inputs.upload-sarif == 'true' }}
146+
uses: github/codeql-action/upload-sarif@v3
147+
with:
148+
sarif_file: ${{ inputs.output-file }}
149+
category: '${{ inputs.category-prefix }}${{ steps.image_details.outputs.safe_name }}'
150+
151+
- name: Archive scan results
152+
if: ${{ !cancelled() && inputs.upload-sarif == 'true' }}
153+
uses: actions/upload-artifact@v4
154+
with:
155+
name: "sarif-${{ steps.image_details.outputs.safe_name }}"
156+
path: ${{ inputs.output-file }}
157+
retention-days: ${{ inputs.retention-days }}

.github/workflows/alpha.yaml

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,15 @@ jobs:
133133
path: .image.env
134134
- name: Scan rqlite for vulnerabilities
135135
id: scan
136-
uses: ./.github/workflows/scan-image-grype.yml
136+
uses: ./.github/actions/scan-image
137137
with:
138-
image: "docker.io/kotsadm/rqlite:${{ steps.dotenv.outputs.RQLITE_TAG }}"
139-
severity-cutoff: 'medium'
140-
fail-build: false
138+
category-prefix: 'image-scan-'
139+
image-ref: "docker.io/kotsadm/rqlite:${{ steps.dotenv.outputs.RQLITE_TAG }}"
140+
only-fixed: 'true'
141141
output-file: 'rqlite-scan-output.sarif'
142-
only-fixed: true
142+
retention-days: '90'
143+
severity-cutoff: 'medium'
144+
upload-sarif: 'true'
143145
- name: Print scan report
144146
run: cat rqlite-scan-output.sarif
145147
- name: Upload scan report
@@ -160,13 +162,16 @@ jobs:
160162
path: .image.env
161163
- name: Scan minio for vulnerabilities
162164
id: scan
163-
uses: ./.github/workflows/scan-image-grype.yml
165+
uses: ./.github/actions/scan-image
164166
with:
165-
image: "docker.io/kotsadm/minio:${{ steps.dotenv.outputs.MINIO_TAG }}"
166-
severity-cutoff: 'medium'
167-
fail-build: false
167+
category-prefix: 'image-scan-'
168+
fail-build: 'false'
169+
image-ref: "docker.io/kotsadm/minio:${{ steps.dotenv.outputs.MINIO_TAG }}"
170+
only-fixed: 'true'
168171
output-file: 'minio-scan-output.sarif'
169-
only-fixed: true
172+
retention-days: '90'
173+
severity-cutoff: 'medium'
174+
upload-sarif: 'true'
170175
- name: Print scan report
171176
run: cat minio-scan-output.sarif
172177
- name: Upload scan report
@@ -187,13 +192,16 @@ jobs:
187192
path: .image.env
188193
- name: Scan dex for vulnerabilities
189194
id: scan
190-
uses: ./.github/workflows/scan-image-grype.yml
195+
uses: ./.github/actions/scan-image
191196
with:
192-
image: "docker.io/kotsadm/dex:${{ steps.dotenv.outputs.DEX_TAG }}"
193-
severity-cutoff: 'medium'
194-
fail-build: false
197+
category-prefix: 'image-scan-'
198+
fail-build: 'false'
199+
image-ref: "docker.io/kotsadm/dex:${{ steps.dotenv.outputs.DEX_TAG }}"
200+
only-fixed: 'true'
195201
output-file: 'dex-scan-output.sarif'
196-
only-fixed: true
202+
retention-days: '90'
203+
severity-cutoff: 'medium'
204+
upload-sarif: 'true'
197205
- name: Print scan report
198206
run: cat dex-scan-output.sarif
199207
- name: Upload scan report
@@ -210,13 +218,16 @@ jobs:
210218
uses: actions/checkout@v4
211219
- name: Scan kurl-proxy for vulnerabilities
212220
id: scan
213-
uses: ./.github/workflows/scan-image-grype.yml
221+
uses: ./.github/actions/scan-image
214222
with:
215-
image: 'docker.io/kotsadm/kurl-proxy:alpha'
216-
severity-cutoff: 'medium'
217-
fail-build: false
223+
category-prefix: 'image-scan-'
224+
fail-build: 'false'
225+
image-ref: 'docker.io/kotsadm/kurl-proxy:alpha'
226+
only-fixed: 'true'
218227
output-file: 'kurl-proxy-scan-output.sarif'
219-
only-fixed: true
228+
retention-days: '90'
229+
severity-cutoff: 'medium'
230+
upload-sarif: 'true'
220231
- name: Print scan report
221232
run: cat kurl-proxy-scan-output.sarif
222233
- name: Upload scan report
@@ -237,13 +248,16 @@ jobs:
237248
path: .image.env
238249
- name: Scan replicated/local-volume-provider for vulnerabilities
239250
id: scan
240-
uses: ./.github/workflows/scan-image-grype.yml
251+
uses: ./.github/actions/scan-image
241252
with:
242-
image: "docker.io/replicated/local-volume-provider:${{ steps.dotenv.outputs.LVP_TAG }}"
243-
severity-cutoff: 'medium'
244-
fail-build: false
253+
category-prefix: 'image-scan-'
254+
fail-build: 'false'
255+
image-ref: "docker.io/replicated/local-volume-provider:${{ steps.dotenv.outputs.LVP_TAG }}"
256+
only-fixed: 'true'
245257
output-file: 'scan-output.sarif'
246-
only-fixed: true
258+
retention-days: '90'
259+
severity-cutoff: 'medium'
260+
upload-sarif: 'true'
247261
- name: Print scan report
248262
run: cat scan-output.sarif
249263
- name: Upload scan report
@@ -260,21 +274,23 @@ jobs:
260274
uses: actions/checkout@v4
261275
- name: Scan kotsadm for vulnerabilities
262276
id: scan
263-
uses: ./.github/workflows/scan-image-grype.yml
277+
uses: ./.github/actions/scan-image
264278
with:
265-
image: 'docker.io/kotsadm/kotsadm:alpha'
266-
severity-cutoff: 'medium'
267-
fail-build: false
279+
category-prefix: 'image-scan-'
280+
fail-build: 'true'
281+
image-ref: 'docker.io/kotsadm/kotsadm:alpha'
282+
only-fixed: 'true'
268283
output-file: 'kotsadm-scan-output.sarif'
269-
only-fixed: true
284+
retention-days: '90'
285+
severity-cutoff: 'medium'
286+
upload-sarif: 'true'
270287
- name: Print scan report
271288
run: cat kotsadm-scan-output.sarif
272289
- name: Upload scan report
273290
uses: github/codeql-action/upload-sarif@v3
274291
with:
275292
sarif_file: kotsadm-scan-output.sarif
276293

277-
278294
scan_kotsadm_migrations:
279295
runs-on: ubuntu-latest
280296
needs: [build-migrations]
@@ -283,16 +299,20 @@ jobs:
283299
uses: actions/checkout@v4
284300
- name: Scan migrations for vulnerabilities
285301
id: scan
286-
uses: ./.github/workflows/scan-image-grype.yml
302+
uses: ./.github/actions/scan-image
287303
with:
288-
image: 'docker.io/kotsadm/kotsadm-migrations:alpha'
289-
severity-cutoff: 'medium'
290-
fail-build: false
304+
category-prefix: 'image-scan-'
305+
fail-build: 'true'
306+
image-ref: 'docker.io/kotsadm/kotsadm-migrations:alpha'
307+
only-fixed: 'true'
291308
output-file: 'kotsadm-migration-scan-output.sarif'
292-
only-fixed: true
309+
retention-days: '90'
310+
severity-cutoff: 'medium'
311+
upload-sarif: 'true'
293312
- name: Print scan report
294313
run: cat kotsadm-migration-scan-output.sarif
295314
- name: Upload scan report
296315
uses: github/codeql-action/upload-sarif@v3
297316
with:
298317
sarif_file: kotsadm-migration-scan-output.sarif
318+

0 commit comments

Comments
 (0)