This repository has been archived by the owner on Sep 9, 2024. It is now read-only.
forked from jupyterhub/zero-to-jupyterhub-k8s
-
Notifications
You must be signed in to change notification settings - Fork 1
221 lines (191 loc) · 8.93 KB
/
vuln-scan.yaml
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
# This is a GitHub workflow defining a set of jobs with a set of steps.
# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions
#
# This workflow use aquasecurity/trivy to scan the images we have published for
# known vulnerabilities. If there are such that can be patched, we let this
# workflow fail to signal that unless we make an exception, which we do for the
# singleuser-sample image only.
#
name: Vuln. scan
on:
pull_request:
paths:
- ".github/workflows/vuln-scan.yaml"
schedule:
# At 00:00 - https://crontab.guru
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
trivy_image_scan:
if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s'
runs-on: ubuntu-20.04
# Write permissions granted for the peter-evans/create-pull-request action
# to push to a branch and create/update a PR
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
matrix:
include:
- image_ref: hub
accept_failure: false
- image_ref: secret-sync
accept_failure: false
- image_ref: network-tools
accept_failure: false
- image_ref: image-awaiter
accept_failure: false
- image_ref: singleuser-sample
accept_failure: false
steps:
- uses: actions/checkout@v2
with:
# chartpress requires git history to set chart version and image tags
# correctly
fetch-depth: 0
- uses: actions/setup-python@v2
with:
python-version: "3.8"
- name: Install chartpress
run: |
pip install chartpress
# charpress --list-images output lines of name:tag format. We use it with
# a search string in matrix.image_ref to find the specific image to scan
# in this job.
- name: Identify image name:tag
id: image
run: |
IMAGE_SPEC=$(
chartpress --list-images \
| grep ${{ matrix.image_ref }}:
)
echo "Identified image: $IMAGE_SPEC"
echo "::set-output name=spec::$IMAGE_SPEC"
echo "::set-output name=name::$(echo $IMAGE_SPEC | sed 's/\(.*\):.*/\1/')"
echo "::set-output name=tag::$(echo $IMAGE_SPEC | sed 's/.*:\(.*\)/\1/')"
- name: Create ./tmp dir
run: mkdir ./tmp
# Action reference: https://github.com/aquasecurity/trivy-action
- name: Scan latest published image
id: scan_1
uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a
with:
image-ref: ${{ steps.image.outputs.spec }}
format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json
output: tmp/scan_1.json
ignore-unfixed: true
exit-code: "1"
# Keep running the subsequent steps of the job, they are made to
# explicitly adjust based on this step's outcome.
continue-on-error: true
# Steps below is only executing if vulnerabilities have been detected.
# -----------------------------------------------------------------------
- name: Rebuild image
id: rebuild
if: steps.scan_1.outcome == 'failure'
run: |
docker build -t rebuilt-image images/${{ matrix.image_ref }}
- name: Scan rebuilt image
id: scan_2
if: steps.rebuild.outcome == 'success'
uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a
with:
image-ref: rebuilt-image
format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json
output: tmp/scan_2.json
ignore-unfixed: true
# Analyze the scan reports. If they differ, we want to proceed and create
# or update a PR. We use a hash from the final scan report as an
# indication to rebuild or not.
- name: Analyze scan reports
id: analyze
if: steps.rebuild.outcome == 'success'
run: |
echo "::set-output name=utc_time::$(date --utc +'%F_%T')"
json_to_misc() {
# Count vulnerabilities
VULNERABILITY_COUNT="$(cat tmp/scan_$1.json | jq -r '[.Results[].Vulnerabilities | select(type != null)] | add | select(. != null) | length')"
echo "VULNERABILITY_COUNT_$1=$VULNERABILITY_COUNT" >> $GITHUB_ENV
# Construct a markdown summary
if [[ "$VULNERABILITY_COUNT" == "0" ]]; then
echo "No vulnerabilities! :tada:" >> tmp/md_summary_$1.md
else
echo "Target | Vuln. ID | Package Name | Installed v. | Fixed v." >> tmp/md_summary_$1.md
echo "-|-|-|-|-" >> tmp/md_summary_$1.md
cat tmp/scan_$1.json | jq -r '.Results[] | select(.Vulnerabilities != null) | .Type + " | " + (.Vulnerabilities[] | .VulnerabilityID + " | " + .PkgName + " | " + .InstalledVersion + " | " + .FixedVersion)' | sort >> tmp/md_summary_$1.md
fi
# Use hack to set a multiline string output
# ref: https://github.com/actions/toolkit/issues/403#issue-593398879
TMP=$(cat tmp/md_summary_$1.md)
TMP="${TMP//'%'/'%25'}"
TMP="${TMP//$'\n'/'%0A'}"
TMP="${TMP//$'\r'/'%0D'}"
echo "::set-output name=md_summary_$1::$TMP"
# Calculate a hash of the markdown summary
HASH=$(cat tmp/md_summary_$1.md | sha1sum)
HASH=${HASH:0:10}
export HASH_$1=$HASH
echo "::set-output name=hash_$1::$HASH"
}
json_to_misc 1
json_to_misc 2
# Did rebuilding the image change anything?
if [ "$HASH_1" == "$HASH_2" ]; then
echo "::set-output name=proceed::no"
echo "No vulnerabilities were patched by rebuilding the image - won't proceed!"
else
echo "::set-output name=proceed::yes"
echo "Vulnerabilities were patched by rebuilding the image - will proceed!"
fi
- name: Describe vulnerabilities
if: steps.rebuild.outcome == 'success'
uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a
with:
image-ref: rebuilt-image
format: table
ignore-unfixed: true
- name: Decision to not proceed
if: steps.analyze.outputs.proceed == 'no'
run: |
echo "::warning::None of the $VULNERABILITY_COUNT_1 vulnerabilities got patched by rebuilding the image :("
continue-on-error: ${{ matrix.accept_failure == true }}
# Steps below are executed if the analyze step decided to proceed.
# -----------------------------------------------------------------------
# Searches for the "# VULN_SCAN_TIME=..." in the Dockerfile and sets a new
# value, which can be used to submit a PR that when merged will trigger a
# rebuild of the image.
- name: Update VULN_SCAN_TIME in Dockerfile
if: steps.analyze.outputs.proceed == 'yes'
run: |
value_to_set="${{ steps.analyze.outputs.utc_time }}"
file_to_update="images/${{ matrix.image_ref }}/Dockerfile"
sed --in-place "s/\(#.*VULN_SCAN_TIME=\)\(.*\)/\1${value_to_set}/" "$file_to_update"
git --no-pager diff --color=always
# The create-pull-request action is smart enough to only create/update a
# PR if there is a change to anything not .gitignored. A change will be
# made only if the analyze steps outputted hash is changed.
#
# ref: https://github.com/peter-evans/create-pull-request
- name: Create or update a PR
if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request'
uses: peter-evans/create-pull-request@dcd5fd746d53dd8de555c0f10bca6c35628be47a
with:
token: "${{ secrets.GITHUB_TOKEN }}"
author: jupyterhub vuln-scan bot <noreply@github.com>
reviewers: consideratio
branch: vuln-scan-${{ matrix.image_ref }}
title: Vulnerability patch in ${{ matrix.image_ref }}
labels: image:rebuild-to-patch-vuln
body: |
A rebuild of `${{ steps.image.outputs.name }}` has been found to influence the detected vulnerabilities! This PR will trigger a rebuild because it has updated a comment in the Dockerfile.
## About
This scan for known vulnerabilities has been made by [aquasecurity/trivy](https://github.com/aquasecurity/trivy). Trivy was configured to filter the vulnerabilities with the following settings:
- ignore-unfixed: `true`
## Before
Before trying to rebuild the image, the following vulnerabilities was detected in `${{ steps.image.outputs.spec }}`.
${{ steps.analyze.outputs.md_summary_1 }}
## After
${{ steps.analyze.outputs.md_summary_2 }}
commit-message: |
Patch known vulnerability in ${{ matrix.image_ref }}