Skip to content

Commit 3b03d45

Browse files
CCM-13343: Trivy Package and Library Scans (#312)
* CCM-13343_Trivy_Package_and_Library_Scans * CCM-13343: Trivy Package and Library Scans * CCM-13343: Trivy Package and Library Scans * CCM-13343: Trivy Package and Library Scans * CCM-13343: Trivy Package and Library Scans * CCM-13343: Trivy Package and Library Scans
1 parent e88f768 commit 3b03d45

File tree

10 files changed

+294
-127
lines changed

10 files changed

+294
-127
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- [ ] I have added tests to cover my changes
2626
- [ ] I have updated the documentation accordingly
2727
- [ ] This PR is a result of pair or mob programming
28+
- [ ] If I have used the 'skip-trivy-package' label I have done so responsibly and in the knowledge that this is being fixed as part of a separate ticket/PR.
2829

2930
---
3031

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: "Trivy IaC Scan"
2+
description: "Scan Terraform IaC using Trivy"
3+
runs:
4+
using: "composite"
5+
steps:
6+
- name: "Trivy Terraform IaC Scan"
7+
shell: bash
8+
run: |
9+
components_exit_code=0
10+
modules_exit_code=0
11+
12+
./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/components || components_exit_code=$?
13+
./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/modules || modules_exit_code=$?
14+
15+
if [ $components_exit_code -ne 0 ] || [ $modules_exit_code -ne 0 ]; then
16+
echo "Trivy misconfigurations detected."
17+
exit 1
18+
fi
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: "Trivy Package Scan"
2+
description: "Scan project packages using Trivy"
3+
runs:
4+
using: "composite"
5+
steps:
6+
- name: "Trivy Package Scan"
7+
shell: bash
8+
run: |
9+
exit_code=0
10+
11+
./scripts/terraform/trivy-scan.sh --mode package . || exit_code=$?
12+
13+
if [ $exit_code -ne 0 ]; then
14+
echo "Trivy has detected package vulnerablilites. Please refer to https://nhsd-confluence.digital.nhs.uk/spaces/RIS/pages/1257636917/PLAT-KOP-012+-+Trivy+Pipeline+Vulnerability+Scanning+Exemption"
15+
exit 1
16+
fi

.github/actions/trivy/action.yaml

Lines changed: 0 additions & 17 deletions
This file was deleted.

.github/workflows/cicd-1-pull-request.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ jobs:
2828
is_version_prerelease: ${{ steps.variables.outputs.is_version_prerelease }}
2929
does_pull_request_exist: ${{ steps.pr_exists.outputs.does_pull_request_exist }}
3030
pr_number: ${{ steps.pr_exists.outputs.pr_number }}
31+
skip_trivy_package: ${{ steps.skip_trivy.outputs.skip_trivy_package }}
3132
steps:
3233
- name: "Checkout code"
3334
uses: actions/checkout@v5
@@ -66,6 +67,26 @@ jobs:
6667
echo "does_pull_request_exist=false" >> $GITHUB_OUTPUT
6768
echo "pr_number=" >> $GITHUB_OUTPUT
6869
fi
70+
- name: "Determine if Trivy package scan should be skipped"
71+
id: skip_trivy
72+
env:
73+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74+
PR_NUMBER: ${{ steps.pr_exists.outputs.pr_number }}
75+
run: |
76+
if [[ -z "$PR_NUMBER" ]]; then
77+
echo "No pull request detected; Trivy package scan will run."
78+
echo "skip_trivy_package=false" >> $GITHUB_OUTPUT
79+
exit 0
80+
fi
81+
82+
labels=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name')
83+
echo "Labels on PR #$PR_NUMBER: $labels"
84+
85+
if echo "$labels" | grep -Fxq 'skip-trivy-package'; then
86+
echo "skip_trivy_package=true" >> $GITHUB_OUTPUT
87+
else
88+
echo "skip_trivy_package=false" >> $GITHUB_OUTPUT
89+
fi
6990
- name: "List variables"
7091
run: |
7192
export BUILD_DATETIME_LONDON="${{ steps.variables.outputs.build_datetime_london }}"
@@ -89,6 +110,7 @@ jobs:
89110
build_epoch: "${{ needs.metadata.outputs.build_epoch }}"
90111
nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}"
91112
python_version: "${{ needs.metadata.outputs.python_version }}"
113+
skip_trivy_package: ${{ needs.metadata.outputs.skip_trivy_package == 'true' }}
92114
terraform_version: "${{ needs.metadata.outputs.terraform_version }}"
93115
version: "${{ needs.metadata.outputs.version }}"
94116
secrets: inherit

.github/workflows/stage-1-commit.yaml

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ on:
2323
description: "Python version, set by the CI/CD pipeline workflow"
2424
required: true
2525
type: string
26+
skip_trivy_package:
27+
description: "Skip Trivy package scan when true"
28+
type: boolean
29+
default: false
2630
terraform_version:
2731
description: "Terraform version, set by the CI/CD pipeline workflow"
2832
required: true
@@ -146,8 +150,11 @@ jobs:
146150
uses: actions/checkout@v5
147151
- name: "Lint Terraform"
148152
uses: ./.github/actions/lint-terraform
149-
trivy:
150-
name: "Trivy Scan"
153+
trivy-iac:
154+
name: "Trivy IaC Scan"
155+
permissions:
156+
contents: read
157+
packages: read
151158
runs-on: ubuntu-latest
152159
timeout-minutes: 10
153160
needs: detect-terraform-changes
@@ -156,18 +163,40 @@ jobs:
156163
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
157164
steps:
158165
- name: "Checkout code"
159-
uses: actions/checkout@v5
160-
- name: Setup NodeJS
161-
uses: actions/setup-node@v4
166+
uses: actions/checkout@v4
167+
- name: "Setup ASDF"
168+
uses: asdf-vm/actions/setup@1902764435ca0dd2f3388eea723a4f92a4eb8302
169+
- name: "Repo setup"
170+
uses: ./.github/actions/node-install
162171
with:
163172
node-version: ${{ inputs.nodejs_version }}
164-
registry-url: 'https://npm.pkg.github.com'
173+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
174+
- name: "Perform Setup"
175+
uses: ./.github/actions/setup
176+
- name: "Trivy IaC Scan"
177+
uses: ./.github/actions/trivy-iac
178+
trivy-package:
179+
if: ${{ !inputs.skip_trivy_package }}
180+
name: "Trivy Package Scan"
181+
permissions:
182+
contents: read
183+
packages: read
184+
runs-on: ubuntu-latest
185+
timeout-minutes: 10
186+
steps:
187+
- name: "Checkout code"
188+
uses: actions/checkout@v4
165189
- name: "Setup ASDF"
166-
uses: asdf-vm/actions/setup@v4
190+
uses: asdf-vm/actions/setup@1902764435ca0dd2f3388eea723a4f92a4eb8302
191+
- name: "Repo setup"
192+
uses: ./.github/actions/node-install
193+
with:
194+
node-version: ${{ inputs.nodejs_version }}
195+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
167196
- name: "Perform Setup"
168197
uses: ./.github/actions/setup
169-
- name: "Trivy Scan"
170-
uses: ./.github/actions/trivy
198+
- name: "Trivy Package Scan"
199+
uses: ./.github/actions/trivy-package
171200
count-lines-of-code:
172201
name: "Count lines of code"
173202
runs-on: ubuntu-latest

.tool-versions

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ java openjdk-25.0.1
1515
# The section below is reserved for Docker image versions.
1616

1717
# TODO: Move this section - consider using a different file for the repository template dependencies.
18-
# docker/ghcr.io/anchore/grype v0.69.1@sha256:d41fcb371d0af59f311e72123dff46900ebd6d0482391b5a830853ee4f9d1a76 # SEE: https://github.com/anchore/grype/pkgs/container/grype
19-
# docker/ghcr.io/anchore/syft v0.92.0@sha256:63c60f0a21efb13e80aa1359ab243e49213b6cc2d7e0f8179da38e6913b997e0 # SEE: https://github.com/anchore/syft/pkgs/container/syft
18+
# docker/ghcr.io/anchore/grype v0.104.3@sha256:d340f4f8b3b7e6e72a6c9c0152f25402ed8a2d7375dba1dfce4e53115242feb6 # SEE: https://github.com/anchore/grype/pkgs/container/grype
19+
# docker/ghcr.io/anchore/syft v1.39.0@sha256:6f13bb010923c33fb197047c8f88888e77071bd32596b3f605d62a133e493ce4 # SEE: https://github.com/anchore/syft/pkgs/container/syft
2020
# docker/ghcr.io/gitleaks/gitleaks v8.18.0@sha256:fd2b5cab12b563d2cc538b14631764a1c25577780e3b7dba71657d58da45d9d9 # SEE: https://github.com/gitleaks/gitleaks/pkgs/container/gitleaks
2121
# docker/ghcr.io/igorshubovych/markdownlint-cli v0.37.0@sha256:fb3e79946fce78e1cde84d6798c6c2a55f2de11fc16606a40d49411e281d950d # SEE: https://github.com/igorshubovych/markdownlint-cli/pkgs/container/markdownlint-cli
2222
# docker/ghcr.io/make-ops-tools/gocloc latest@sha256:6888e62e9ae693c4ebcfed9f1d86c70fd083868acb8815fe44b561b9a73b5032 # SEE: https://github.com/make-ops-tools/gocloc/pkgs/container/gocloc

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/terraform/trivy-scan.sh

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#!/usr/bin/env bash
2+
3+
# WARNING: Please DO NOT edit this file! It is maintained in the Repository Template (https://github.com/NHSDigital/nhs-notify-repository-template). Raise a PR instead.
4+
5+
set -euo pipefail
6+
7+
function usage() {
8+
cat <<'EOF'
9+
Usage: ./scripts/terraform/trivy-scan.sh --mode <iac|package> [directory]
10+
11+
Options:
12+
--mode, -m Scan type to run. Accepts "iac" or "package" (required).
13+
--help, -h Show this message.
14+
[directory] Directory to scan. Defaults to the repository root.
15+
16+
Environment variables:
17+
FORCE_USE_DOCKER=true Force execution through Docker even if Trivy is installed locally.
18+
VERBOSE=true Enable bash -x tracing.
19+
EOF
20+
}
21+
22+
function main() {
23+
cd "$(git rev-parse --show-toplevel)"
24+
25+
local scan_mode=""
26+
local dir_to_scan="."
27+
28+
while [[ $# -gt 0 ]]; do
29+
case "$1" in
30+
--mode|-m)
31+
if [[ $# -lt 2 ]]; then
32+
echo "Error: --mode requires an argument." >&2
33+
usage
34+
exit 1
35+
fi
36+
scan_mode="$2"
37+
shift 2
38+
;;
39+
--help|-h)
40+
usage
41+
exit 0
42+
;;
43+
--)
44+
shift
45+
break
46+
;;
47+
-*)
48+
echo "Unknown option: $1" >&2
49+
usage
50+
exit 1
51+
;;
52+
*)
53+
dir_to_scan="$1"
54+
shift
55+
;;
56+
esac
57+
done
58+
59+
if [[ $# -gt 0 ]]; then
60+
dir_to_scan="$1"
61+
fi
62+
63+
if [[ -z "$scan_mode" ]]; then
64+
echo "Error: --mode must be provided (iac|package)." >&2
65+
usage
66+
exit 1
67+
fi
68+
69+
case "$scan_mode" in
70+
iac|package)
71+
;;
72+
*)
73+
echo "Error: unknown mode '$scan_mode'. Expected 'iac' or 'package'." >&2
74+
usage
75+
exit 1
76+
;;
77+
esac
78+
79+
if command -v trivy > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then
80+
run-trivy-natively "$scan_mode" "$dir_to_scan"
81+
else
82+
run-trivy-in-docker "$scan_mode" "$dir_to_scan"
83+
fi
84+
}
85+
86+
function run-trivy-natively() {
87+
local scan_mode="$1"
88+
local dir_to_scan="$2"
89+
90+
echo "Trivy found locally, running natively"
91+
echo "Running Trivy ($scan_mode) on directory: $dir_to_scan"
92+
93+
if execute-trivy-command "$scan_mode" "$dir_to_scan"; then
94+
check-trivy-status 0
95+
else
96+
local status=$?
97+
check-trivy-status "$status"
98+
fi
99+
}
100+
101+
function run-trivy-in-docker() {
102+
# shellcheck disable=SC1091
103+
source ./scripts/docker/docker.lib.sh
104+
105+
local scan_mode="$1"
106+
local dir_to_scan="$2"
107+
108+
# shellcheck disable=SC2155
109+
local image=$(name=aquasec/trivy docker-get-image-version-and-pull)
110+
111+
echo "Trivy not found locally, running in Docker Container"
112+
echo "Running Trivy ($scan_mode) on directory: $dir_to_scan"
113+
114+
if execute-trivy-in-docker "$image" "$scan_mode" "$dir_to_scan"; then
115+
check-trivy-status 0
116+
else
117+
local status=$?
118+
check-trivy-status "$status"
119+
fi
120+
}
121+
122+
function execute-trivy-command() {
123+
local scan_mode="$1"
124+
local dir_to_scan="$2"
125+
126+
if [[ "$scan_mode" == "iac" ]]; then
127+
trivy config \
128+
--config scripts/config/trivy.yaml \
129+
--tf-exclude-downloaded-modules \
130+
"$dir_to_scan"
131+
else
132+
trivy \
133+
--config scripts/config/trivy.yaml \
134+
fs "$dir_to_scan" \
135+
--scanners vuln \
136+
--severity HIGH,CRITICAL \
137+
--include-dev-deps
138+
fi
139+
}
140+
141+
function execute-trivy-in-docker() {
142+
local image="$1"
143+
local scan_mode="$2"
144+
local dir_to_scan="$3"
145+
146+
if [[ "$scan_mode" == "iac" ]]; then
147+
docker run --rm --platform linux/amd64 \
148+
--volume "$PWD":/workdir \
149+
--workdir /workdir \
150+
"$image" \
151+
config \
152+
--config scripts/config/trivy.yaml \
153+
--tf-exclude-downloaded-modules \
154+
"$dir_to_scan"
155+
else
156+
docker run --rm --platform linux/amd64 \
157+
--volume "$PWD":/workdir \
158+
--workdir /workdir \
159+
"$image" \
160+
--config scripts/config/trivy.yaml \
161+
fs "$dir_to_scan" \
162+
--scanners vuln \
163+
--severity HIGH,CRITICAL \
164+
--include-dev-deps
165+
fi
166+
}
167+
168+
function check-trivy-status() {
169+
local status="$1"
170+
171+
if [[ "$status" -eq 0 ]]; then
172+
echo "Trivy completed successfully."
173+
return 0
174+
fi
175+
176+
echo "Trivy found issues."
177+
exit "$status"
178+
}
179+
180+
function is-arg-true() {
181+
if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then
182+
return 0
183+
else
184+
return 1
185+
fi
186+
}
187+
188+
# ==============================================================================
189+
190+
is-arg-true "${VERBOSE:-false}" && set -x
191+
192+
main "$@"
193+
194+
exit 0

0 commit comments

Comments
 (0)