Skip to content

Commit

Permalink
OCI-attach reports from clair-scan 0.2 Task
Browse files Browse the repository at this point in the history
This attaches the "clair"-formatted output of the `clair-action` command
to the scanned image. Contrary to the `sast-snyk-check` Task only a
single variant of attachment method is supported, based on the registry
support. For quay.io OCI Distribution 1.1. Referrers API will be used.

The `clair-action` needs to be run twice as the Rego rules executed in
the `conftest-vulnerabilities` require the "quay" format, which does not
include any date information, whereas the (future) EC policy Rego rules
require the "clair" format, which does.

Resolves: https://issues.redhat.com/browse/EC-837
  • Loading branch information
zregvart committed Sep 30, 2024
1 parent e95aec6 commit 0ff6e35
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 3 deletions.
51 changes: 48 additions & 3 deletions task/clair-scan/0.2/clair-scan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,22 +92,32 @@ spec:
value: $(params.image-url)
- name: IMAGE_DIGEST
value: $(params.image-digest)
workingDir: /tekton/home
script: |
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
imagewithouttag=$(echo -n $IMAGE_URL | sed "s/\(.*\):.*/\1/")
images_processed_template='{"image": {"pullspec": "'"$IMAGE_URL"'", "digests": [%s]}}'
digests_processed=()
for sha_file in /tekton/home/image-manifest-*.sha; do
clair_report() {
{ clair-action report --image-ref="$1" --db-path=/tmp/matcher.db --format=quay | tee clair-result-$arch.json; } && \
{ clair-action report --image-ref="$1" --db-path=/tmp/matcher.db --format=clair > clair-report-$arch.json; }
}
for sha_file in image-manifest-*.sha; do
if [ -e "$sha_file" ]; then
arch_sha=$(cat "$sha_file")
arch=$(basename "$sha_file" | sed 's/image-manifest-//;s/.sha//')
arch_specific_digest="$imagewithouttag@$arch_sha"
echo "Running clair-action on $arch image manifest."
# run the scan for each image manifest in the image index
clair-action report --image-ref=$arch_specific_digest --db-path=/tmp/matcher.db --format=quay | tee /tekton/home/clair-result-$arch.json || true
clair_report "$arch_specific_digest" || true
digests_processed+=("\"$arch_sha\"")
fi
Expand All @@ -117,7 +127,42 @@ spec:
# add the image_index to the processed digests list and store the result in a file
images_processed=$(echo "${images_processed_template/\[%s]/[$digests_processed_string]}")
echo "$images_processed" > /tekton/home/images-processed.json
echo "$images_processed" > images-processed.json
- name: oci-attach-report
image: quay.io/konflux-ci/oras:latest@sha256:56589c1c9132aeaccffad2fc2fd1c3b612741b961e177a91abeb81cb0d859ff2
workingDir: /tekton/home
env:
- name: IMAGE_URL
value: $(params.image-url)
script: |
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
if ! compgen -G "clair-report-*.json" > /dev/null; then
echo 'No Clair reports generated. Skipping upload.'
exit 0
fi
echo "Selecting auth"
select-oci-auth $IMAGE_URL > $HOME/auth.json
base_image="${IMAGE_URL/:*/}"
arch() {
report_file="$1"
arch="${report_file/*-}"
echo "${arch/.json/}"
}
MEDIA_TYPE='application/vnd.redhat.clair-report+json'
for f in clair-report-*.json; do
image_ref="${base_image}@$(cat image-manifest-$(arch "$f").sha)"
echo "Attaching $f to ${image_ref}"
oras attach --no-tty --registry-config "$HOME/auth.json" --artifact-type "${MEDIA_TYPE}" "${image_ref}" "$f:${MEDIA_TYPE}"
done
- name: conftest-vulnerabilities
image: quay.io/redhat-appstudio/konflux-test:v1.4.7@sha256:cf6808a3bd605630a5d9f20595ff7c43f8645c00381219d32f5a11e88fe37072
# per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting
Expand Down
156 changes: 156 additions & 0 deletions task/clair-scan/0.2/spec/clair_scan_spec.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#!/bin/env bash

set -o errexit
set -o pipefail
set -o nounset

eval "$(shellspec - -c) exit 1"

task_path=clair-scan.yaml

if [[ -f "../${task_path}" ]]; then
task_path="../${task_path}"
fi


extract_script() {
script="$(mktemp --tmpdir script_XXXXXXXXXX.sh)"
yq -r ".spec.steps[] | select(.name == \"$1\").script" "${task_path}" > "${script}"
chmod +x "${script}"

echo "${script}"
}

# array containing files/directories to remove on test exit
cleanup=()
trap 'rm -rf "${cleanup[@]}"' EXIT

# Extract the get-vulnerabilities Step script so we can test it
get_vulnerabilities_script="$(extract_script get-vulnerabilities)"
cleanup+=("${get_vulnerabilities_script}")

testdir() {
testdir="$(mktemp -d)" && cleanup+=("${testdir}") && cd "${testdir}"

AfterEach 'rm -rf "$testdir"'
}

Describe "get vulnerabilities"
BeforeEach testdir

export IMAGE_URL=registry.io/repository/image:tag
export IMAGE_DIGEST=sha256:f0cacc1a

It "generates reports and images-processed.json"
Mock clair-action
clair_action_args+=("$*")
%preserve clair_action_args
# expecting the --format parameter to be the last one
echo "report in ${clair_action_args[-1]#*--format=} format"
End
echo "sha256:f0cacc1a" > image-manifest-amd64.sha

When call "${get_vulnerabilities_script}"
The output should eq "Running clair-action on amd64 image manifest.
report in quay format"
The contents of file "images-processed.json" should equal '{"image": {"pullspec": "registry.io/repository/image:tag", "digests": ["sha256:f0cacc1a"]}}'
The contents of file "clair-result-amd64.json" should equal 'report in quay format'
The contents of file "clair-report-amd64.json" should equal 'report in clair format'
The variable clair_action_args[@] should eq "report --image-ref=registry.io/repository/image@sha256:f0cacc1a --db-path=/tmp/matcher.db --format=quay "\
"report --image-ref=registry.io/repository/image@sha256:f0cacc1a --db-path=/tmp/matcher.db --format=clair"
End

It "fails in clair-action quay report"
Mock clair-action
clair_action_args+=("$*")
%preserve clair_action_args
[[ "$*" == *--format=quay* ]] && echo "didn't work out" && exit 1
End
echo "sha256:f0cacc1a" > image-manifest-amd64.sha

When call "${get_vulnerabilities_script}"
The output should eq "Running clair-action on amd64 image manifest.
didn't work out"
The contents of file "images-processed.json" should equal '{"image": {"pullspec": "registry.io/repository/image:tag", "digests": ["sha256:f0cacc1a"]}}'
The contents of file "clair-result-amd64.json" should equal "didn't work out"
The file "clair-report-amd64.json" should not exist
The variable clair_action_args[@] should eq "report --image-ref=registry.io/repository/image@sha256:f0cacc1a --db-path=/tmp/matcher.db --format=quay"
End
End

# Extract the oci-attach-report Step script so we can test it
oci_attach_report_script="$(extract_script oci-attach-report)"
cleanup+=("${oci_attach_report_script}")

oras_attach() {
echo "attach --no-tty --registry-config $HOME/auth.json --artifact-type application/vnd.redhat.clair-report+json registry.io/repository/image@$1 $2:application/vnd.redhat.clair-report+json"
}

Describe "OCI attach report"
BeforeEach testdir

export IMAGE_URL=registry.io/repository/image:tag

It "skips attachments if no reports generated"
Mock select-oci-auth
echo select-oci-auth should not be called
End

Mock oras
echo oras should not be called
End

When call "${oci_attach_report_script}"
The output should eq "No Clair reports generated. Skipping upload."
End

It "attaches for single architecture"
export HOME="${testdir}"

Mock select-oci-auth
echo selected auth
End

Mock oras
oras_args+=("$*")
%preserve oras_args
End

echo "sha256:f0cacc1a" > image-manifest-amd64.sha
touch clair-report-amd64.json

When call "${oci_attach_report_script}"
The output should eq "Selecting auth
Attaching clair-report-amd64.json to registry.io/repository/image@sha256:f0cacc1a"
The contents of file "auth.json" should equal "selected auth"
The variable oras_args[@] should eq "$(oras_attach sha256:f0cacc1a clair-report-amd64.json)"
End

It "attaches for multiple architecture"
export HOME="${testdir}"

Mock select-oci-auth
echo selected auth
End

Mock oras
oras_args+=("$*")
%preserve oras_args
End

echo "sha256:f0cacc1a" > image-manifest-amd64.sha
echo "sha256:cc1af0ca" > image-manifest-arm64.sha
echo "sha256:f01acacc" > image-manifest-ppc64le.sha
touch clair-report-{amd64,arm64,ppc64le}.json

When call "${oci_attach_report_script}"
The output should eq "Selecting auth
Attaching clair-report-amd64.json to registry.io/repository/image@sha256:f0cacc1a
Attaching clair-report-arm64.json to registry.io/repository/image@sha256:cc1af0ca
Attaching clair-report-ppc64le.json to registry.io/repository/image@sha256:f01acacc"
The contents of file "auth.json" should equal "selected auth"
The variable oras_args[@] should eq "$(oras_attach sha256:f0cacc1a clair-report-amd64.json) "\
"$(oras_attach sha256:cc1af0ca clair-report-arm64.json) "\
"$(oras_attach sha256:f01acacc clair-report-ppc64le.json)"
End
End

0 comments on commit 0ff6e35

Please sign in to comment.