Skip to content

Commit 394db65

Browse files
committed
[skipci] Show trivy errors when scanning images
1 parent af85990 commit 394db65

1 file changed

Lines changed: 200 additions & 91 deletions

File tree

tools/scan-images.sh

Lines changed: 200 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,213 @@
11
#!/usr/bin/env bash
22
set -eo pipefail
33

4-
# Check correct usage
5-
if [[ ! $2 ]]; then
6-
echo "Usage: scan-images.sh <os-distribution> <image-tag>"
4+
# Disable telemetry and version check:
5+
# https://github.com/aquasecurity/trivy/discussions/8945
6+
export TRIVY_DISABLE_TELEMETRY=true
7+
export TRIVY_SKIP_VERSION_CHECK=true
8+
9+
# Global variables
10+
scan_common_args=" \
11+
--exit-code 1 \
12+
--scanners vuln \
13+
--format json \
14+
--severity HIGH,CRITICAL \
15+
--ignore-unfixed \
16+
--db-repository ghcr.io/aquasecurity/trivy-db:2 \
17+
--db-repository public.ecr.aws/aquasecurity/trivy-db \
18+
--java-db-repository ghcr.io/aquasecurity/trivy-java-db:1 \
19+
--java-db-repository public.ecr.aws/aquasecurity/trivy-java-db "
20+
21+
# Print usage instructions and error with wrong inputs
22+
usage() {
23+
echo "Usage: scan-images.sh <os-distribution> <image-tag> [--sbom]"
724
exit 2
8-
fi
9-
10-
set -u
11-
12-
# Check that trivy is installed
13-
if ! trivy --version; then
14-
echo 'Please install trivy: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.68.2'
15-
fi
16-
17-
# Clear any previous outputs
18-
rm -rf image-scan-output
19-
20-
# Make a fresh output directory
21-
mkdir -p image-scan-output
22-
23-
# Get built container images
24-
images=$(docker image ls \
25-
--filter "reference=ark.stackhpc.com/stackhpc-dev/*:$2*" \
26-
--format "{{.Repository}}:{{.Tag}}")
27-
28-
# Save list of images to file
29-
echo "$images" > "$1-scanned-container-images.txt"
30-
31-
# Ensure output files exist
32-
touch image-scan-output/clean-images.txt image-scan-output/dirty-images.txt image-scan-output/critical-images.txt
33-
34-
# If Trivy detects no vulnerabilities, add the image name to clean-images.txt.
35-
# If there are vulnerabilities detected, add it to dirty-images.txt and
36-
# generate a csv summary
37-
# If the image contains at least one critical vulnerabilities, add it to
38-
# critical-images.txt
39-
for image in $images; do
40-
filename=$(basename $image | sed 's/:/\./g')
41-
imagename=$(echo $filename | cut -d "." -f 1 | sed 's/-/_/g')
42-
global_vulnerabilities=$(yq .global_allowed_vulnerabilities[] src/kayobe-config/etc/kayobe/trivy/allowed-vulnerabilities.yml)
43-
image_vulnerabilities=$(yq .$imagename'_allowed_vulnerabilities[]' src/kayobe-config/etc/kayobe/trivy/allowed-vulnerabilities.yml)
25+
}
26+
27+
# Check dependencies are installed, print installation instructions otherwise
28+
check_deps_installed() {
29+
if ! trivy --version > /dev/null; then
30+
echo 'Please install trivy: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin v0.68.2'
31+
exit 1
32+
fi
33+
if ! yq --version > /dev/null; then
34+
echo 'Please install yq: sudo dnf/apt install yq'
35+
exit 1
36+
fi
37+
}
38+
39+
# Prepare output files
40+
file_prep() {
41+
rm -rf image-scan-output
42+
mkdir -p image-scan-output
43+
touch image-scan-output/clean-images.txt image-scan-output/high-images.txt image-scan-output/critical-images.txt
44+
}
45+
46+
# Gather image lists
47+
get_images() {
48+
local output_file="$1-scanned-container-images.txt"
49+
50+
docker image ls \
51+
--filter "reference=ark.stackhpc.com/stackhpc-dev/*:$2*" \
52+
--format "{{.Repository}}:{{.Tag}}" \
53+
> "$output_file"
54+
55+
cat "$output_file"
56+
}
57+
58+
# Generate ignored vulnerabilities file
59+
generate_trivy_ignore() {
60+
local imagename=$1
61+
local global_vulnerabilities
62+
global_vulnerabilities=$(yq .global_allowed_vulnerabilities[] src/kayobe-config/etc/kayobe/trivy/allowed-vulnerabilities.yml 2> /dev/null)
63+
local image_vulnerabilities
64+
image_vulnerabilities=$(yq ."$imagename"'_allowed_vulnerabilities[]' src/kayobe-config/etc/kayobe/trivy/allowed-vulnerabilities.yml 2> /dev/null)
65+
4466
touch .trivyignore
4567
for vulnerability in $global_vulnerabilities; do
46-
echo $vulnerability >> .trivyignore
68+
echo "$vulnerability" >> .trivyignore
4769
done
4870
for vulnerability in $image_vulnerabilities; do
49-
echo $vulnerability >> .trivyignore
71+
echo "$vulnerability" >> .trivyignore
5072
done
51-
if $(trivy image \
52-
--quiet \
53-
--exit-code 1 \
54-
--scanners vuln \
55-
--format json \
56-
--severity HIGH,CRITICAL \
57-
--output image-scan-output/${filename}.json \
58-
--ignore-unfixed \
59-
--db-repository ghcr.io/aquasecurity/trivy-db:2 \
60-
--db-repository public.ecr.aws/aquasecurity/trivy-db \
61-
--java-db-repository ghcr.io/aquasecurity/trivy-java-db:1 \
62-
--java-db-repository public.ecr.aws/aquasecurity/trivy-java-db \
63-
$image); then
64-
# Clean up the output file for any images with no vulnerabilities
65-
rm -f image-scan-output/${filename}.json
66-
67-
# Add the image to the clean list
73+
}
74+
75+
# Put results into CSV
76+
generate_summary_csv() {
77+
local scan="$1"
78+
local summary="$2"
79+
80+
echo '"PkgName","PkgPath","PkgID","VulnerabilityID","FixedVersion","PrimaryURL","Severity"' > "$summary"
81+
82+
jq -r '.Results[]
83+
| select(.Vulnerabilities)
84+
| .Vulnerabilities
85+
| map(select(.PkgName | test("kernel") | not ))
86+
| group_by(.VulnerabilityID)
87+
| map(
88+
[
89+
(map(.PkgName) | unique | join(";")),
90+
(map(.PkgPath | select( . != null )) | join(";")),
91+
.[0].PkgID,
92+
.[0].VulnerabilityID,
93+
.[0].FixedVersion,
94+
.[0].PrimaryURL,
95+
.[0].Severity
96+
]
97+
)
98+
| .[]
99+
| @csv' "$scan" >> "$summary"
100+
}
101+
102+
# Categorise images based on severity
103+
categorise_image() {
104+
local summary="$1"
105+
local image="$2"
106+
107+
if [ "$(grep "CRITICAL" "$summary" -c)" -gt 0 ]; then
108+
echo "${image}" >> image-scan-output/critical-images.txt
109+
else
110+
echo "${image}" >> image-scan-output/high-images.txt
111+
fi
112+
}
113+
114+
# Generate SBOM, return correct scan command for SBOM
115+
generate_sbom() {
116+
local sbom="$1"
117+
local scan="$2"
118+
local image="$3"
119+
trivy image \
120+
--debug \
121+
--format spdx-json \
122+
--output "$sbom" \
123+
"$image" &> "$sbom.log"
124+
if [ ! -e "$sbom" ]; then
125+
(
126+
echo "ERROR: trivy image didn't produce the sbom file $sbom for $image" 1>&2
127+
echo "==== trivy log ===="
128+
cat "$sbom.log"
129+
) 1>&2
130+
exit 1
131+
elif grep -q FATAL "$sbom.log"; then
132+
(
133+
echo "ERROR: trivy image encountered a fatal error producing $sbom for $image"
134+
echo "==== trivy log ===="
135+
cat "$sbom.log"
136+
echo "==== sbom.json ===="
137+
cat "$sbom"
138+
) 1>&2
139+
exit 1
140+
else
141+
echo "trivy sbom $scan_common_args --output $scan $sbom"
142+
fi
143+
}
144+
145+
# Scan images, generate SBOMs if requested
146+
scan_image() {
147+
local image=$1
148+
local filename
149+
filename=$(basename "$image" | sed 's/:/\./g')
150+
local imagename
151+
imagename=$(echo "$filename" | cut -d "." -f 1 | sed 's/-/_/g')
152+
local sbom="image-scan-output/${imagename}/${filename}-sbom.json"
153+
local scan="image-scan-output/${imagename}/${filename}-scan.json"
154+
local summary="image-scan-output/${imagename}/${filename}-summary.csv"
155+
156+
mkdir -p "image-scan-output/$imagename"
157+
generate_trivy_ignore "$imagename"
158+
159+
# If SBOM is required, generate it first and scan the results, otherwise we
160+
# scan the image directly.
161+
if $generate_sbom; then
162+
echo "Generating SBOM for $imagename"
163+
scan_command="$(generate_sbom "$sbom" "$scan" "$image")"
164+
else
165+
scan_command="trivy image $scan_common_args --output $scan $image"
166+
fi
167+
168+
# Run scan against image or SBOM, format output. If no results, delete files.
169+
echo "Scanning $imagename for vulnerabilities"
170+
if $scan_command >& "$scan.log"; then
171+
rm -f "$scan"
68172
echo "${image}" >> image-scan-output/clean-images.txt
173+
elif [ ! -f "$scan" ]; then
174+
(
175+
echo "ERROR: trivy scan encountered an error producing $scan"
176+
echo "Command: $scan_command"
177+
echo "==== trivy log ===="
178+
cat "$scan.log"
179+
if $generate_sbom; then
180+
echo "==== sbom.json ===="
181+
cat "$sbom"
182+
fi
183+
) 1>&2
184+
exit 1
69185
else
186+
generate_summary_csv "$scan" "$summary"
187+
categorise_image "$summary" "$image"
188+
fi
189+
}
190+
191+
# Main function
192+
main() {
193+
if [[ ! $2 ]]; then
194+
usage
195+
fi
70196

71-
# Write a header for the summary CSV
72-
echo '"PkgName","PkgPath","PkgID","VulnerabilityID","FixedVersion","PrimaryURL","Severity"' > image-scan-output/${filename}.summary.csv
73-
74-
# Write the summary CSV data
75-
jq -r '.Results[]
76-
| select(.Vulnerabilities)
77-
| .Vulnerabilities
78-
# Ignore packages with "kernel" in the PkgName
79-
| map(select(.PkgName | test("kernel") | not ))
80-
| group_by(.VulnerabilityID)
81-
| map(
82-
[
83-
(map(.PkgName) | unique | join(";")),
84-
(map(.PkgPath | select( . != null )) | join(";")),
85-
.[0].PkgID,
86-
.[0].VulnerabilityID,
87-
.[0].FixedVersion,
88-
.[0].PrimaryURL,
89-
.[0].Severity
90-
]
91-
)
92-
| .[]
93-
| @csv' image-scan-output/${filename}.json >> image-scan-output/${filename}.summary.csv
94-
95-
if [ $(grep "CRITICAL" image-scan-output/${filename}.summary.csv -c) -gt 0 ]; then
96-
# If the image contains critical vulnerabilities, add the image to critical list
97-
echo "${image}" >> image-scan-output/critical-images.txt
98-
else
99-
# Otherwise, add the image to the dirty list
100-
echo "${image}" >> image-scan-output/dirty-images.txt
101-
fi
197+
generate_sbom=false
198+
if [[ "$3" == "--sbom" ]]; then
199+
generate_sbom=true
102200
fi
103-
rm .trivyignore
104-
done
201+
202+
set -u
203+
204+
check_deps_installed
205+
file_prep
206+
207+
images=$(get_images "$1" "$2")
208+
for image in $images; do
209+
scan_image "$image"
210+
done
211+
}
212+
213+
main "$@"

0 commit comments

Comments
 (0)