Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [ ] I have added tests to cover my changes
- [ ] I have updated the documentation accordingly
- [ ] This PR is a result of pair or mob programming
- [ ] 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.

---

Expand Down
18 changes: 18 additions & 0 deletions .github/actions/trivy-iac/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: "Trivy IaC Scan"
description: "Scan Terraform IaC using Trivy"
runs:
using: "composite"
steps:
- name: "Trivy Terraform IaC Scan"
shell: bash
run: |
components_exit_code=0
modules_exit_code=0
./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/components || components_exit_code=$?
./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/modules || modules_exit_code=$?
if [ $components_exit_code -ne 0 ] || [ $modules_exit_code -ne 0 ]; then
echo "Trivy misconfigurations detected."
exit 1
fi
16 changes: 16 additions & 0 deletions .github/actions/trivy-package/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: "Trivy Package Scan"
description: "Scan project packages using Trivy"
runs:
using: "composite"
steps:
- name: "Trivy Package Scan"
shell: bash
run: |
exit_code=0
./scripts/terraform/trivy-scan.sh --mode package . || exit_code=$?
if [ $exit_code -ne 0 ]; then
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"
exit 1
fi
17 changes: 0 additions & 17 deletions .github/actions/trivy/action.yaml

This file was deleted.

22 changes: 22 additions & 0 deletions .github/workflows/cicd-1-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
is_version_prerelease: ${{ steps.variables.outputs.is_version_prerelease }}
does_pull_request_exist: ${{ steps.pr_exists.outputs.does_pull_request_exist }}
pr_number: ${{ steps.pr_exists.outputs.pr_number }}
skip_trivy_package: ${{ steps.skip_trivy.outputs.skip_trivy_package }}
steps:
- name: "Checkout code"
uses: actions/checkout@v5
Expand Down Expand Up @@ -66,6 +67,26 @@ jobs:
echo "does_pull_request_exist=false" >> $GITHUB_OUTPUT
echo "pr_number=" >> $GITHUB_OUTPUT
fi
- name: "Determine if Trivy package scan should be skipped"
id: skip_trivy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.pr_exists.outputs.pr_number }}
run: |
if [[ -z "$PR_NUMBER" ]]; then
echo "No pull request detected; Trivy package scan will run."
echo "skip_trivy_package=false" >> $GITHUB_OUTPUT
exit 0
fi

labels=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name')
echo "Labels on PR #$PR_NUMBER: $labels"

if echo "$labels" | grep -Fxq 'skip-trivy-package'; then
echo "skip_trivy_package=true" >> $GITHUB_OUTPUT
else
echo "skip_trivy_package=false" >> $GITHUB_OUTPUT
fi
- name: "List variables"
run: |
export BUILD_DATETIME_LONDON="${{ steps.variables.outputs.build_datetime_london }}"
Expand All @@ -89,6 +110,7 @@ jobs:
build_epoch: "${{ needs.metadata.outputs.build_epoch }}"
nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}"
python_version: "${{ needs.metadata.outputs.python_version }}"
skip_trivy_package: ${{ needs.metadata.outputs.skip_trivy_package == 'true' }}
terraform_version: "${{ needs.metadata.outputs.terraform_version }}"
version: "${{ needs.metadata.outputs.version }}"
secrets: inherit
Expand Down
47 changes: 38 additions & 9 deletions .github/workflows/stage-1-commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ on:
description: "Python version, set by the CI/CD pipeline workflow"
required: true
type: string
skip_trivy_package:
description: "Skip Trivy package scan when true"
type: boolean
default: false
terraform_version:
description: "Terraform version, set by the CI/CD pipeline workflow"
required: true
Expand Down Expand Up @@ -146,8 +150,11 @@ jobs:
uses: actions/checkout@v5
- name: "Lint Terraform"
uses: ./.github/actions/lint-terraform
trivy:
name: "Trivy Scan"
trivy-iac:
name: "Trivy IaC Scan"
permissions:
contents: read
packages: read
runs-on: ubuntu-latest
timeout-minutes: 10
needs: detect-terraform-changes
Expand All @@ -156,18 +163,40 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: "Checkout code"
uses: actions/checkout@v5
- name: Setup NodeJS
uses: actions/setup-node@v4
uses: actions/checkout@v4
- name: "Setup ASDF"
uses: asdf-vm/actions/setup@1902764435ca0dd2f3388eea723a4f92a4eb8302
- name: "Repo setup"
uses: ./.github/actions/node-install
with:
node-version: ${{ inputs.nodejs_version }}
registry-url: 'https://npm.pkg.github.com'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Perform Setup"
uses: ./.github/actions/setup
- name: "Trivy IaC Scan"
uses: ./.github/actions/trivy-iac
trivy-package:
if: ${{ !inputs.skip_trivy_package }}
name: "Trivy Package Scan"
permissions:
contents: read
packages: read
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: "Checkout code"
uses: actions/checkout@v4
- name: "Setup ASDF"
uses: asdf-vm/actions/setup@v4
uses: asdf-vm/actions/setup@1902764435ca0dd2f3388eea723a4f92a4eb8302
- name: "Repo setup"
uses: ./.github/actions/node-install
with:
node-version: ${{ inputs.nodejs_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Perform Setup"
uses: ./.github/actions/setup
- name: "Trivy Scan"
uses: ./.github/actions/trivy
- name: "Trivy Package Scan"
uses: ./.github/actions/trivy-package
count-lines-of-code:
name: "Count lines of code"
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ java openjdk-25.0.1
# The section below is reserved for Docker image versions.

# TODO: Move this section - consider using a different file for the repository template dependencies.
# docker/ghcr.io/anchore/grype v0.69.1@sha256:d41fcb371d0af59f311e72123dff46900ebd6d0482391b5a830853ee4f9d1a76 # SEE: https://github.com/anchore/grype/pkgs/container/grype
# docker/ghcr.io/anchore/syft v0.92.0@sha256:63c60f0a21efb13e80aa1359ab243e49213b6cc2d7e0f8179da38e6913b997e0 # SEE: https://github.com/anchore/syft/pkgs/container/syft
# docker/ghcr.io/anchore/grype v0.104.3@sha256:d340f4f8b3b7e6e72a6c9c0152f25402ed8a2d7375dba1dfce4e53115242feb6 # SEE: https://github.com/anchore/grype/pkgs/container/grype
# docker/ghcr.io/anchore/syft v1.39.0@sha256:6f13bb010923c33fb197047c8f88888e77071bd32596b3f605d62a133e493ce4 # SEE: https://github.com/anchore/syft/pkgs/container/syft
# docker/ghcr.io/gitleaks/gitleaks v8.18.0@sha256:fd2b5cab12b563d2cc538b14631764a1c25577780e3b7dba71657d58da45d9d9 # SEE: https://github.com/gitleaks/gitleaks/pkgs/container/gitleaks
# docker/ghcr.io/igorshubovych/markdownlint-cli v0.37.0@sha256:fb3e79946fce78e1cde84d6798c6c2a55f2de11fc16606a40d49411e281d950d # SEE: https://github.com/igorshubovych/markdownlint-cli/pkgs/container/markdownlint-cli
# docker/ghcr.io/make-ops-tools/gocloc latest@sha256:6888e62e9ae693c4ebcfed9f1d86c70fd083868acb8815fe44b561b9a73b5032 # SEE: https://github.com/make-ops-tools/gocloc/pkgs/container/gocloc
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

194 changes: 194 additions & 0 deletions scripts/terraform/trivy-scan.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#!/usr/bin/env bash

# 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.

set -euo pipefail

function usage() {
cat <<'EOF'
Usage: ./scripts/terraform/trivy-scan.sh --mode <iac|package> [directory]

Options:
--mode, -m Scan type to run. Accepts "iac" or "package" (required).
--help, -h Show this message.
[directory] Directory to scan. Defaults to the repository root.

Environment variables:
FORCE_USE_DOCKER=true Force execution through Docker even if Trivy is installed locally.
VERBOSE=true Enable bash -x tracing.
EOF
}

function main() {
cd "$(git rev-parse --show-toplevel)"

local scan_mode=""
local dir_to_scan="."

while [[ $# -gt 0 ]]; do
case "$1" in
--mode|-m)
if [[ $# -lt 2 ]]; then
echo "Error: --mode requires an argument." >&2
usage
exit 1
fi
scan_mode="$2"
shift 2
;;
--help|-h)
usage
exit 0
;;
--)
shift
break
;;
-*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
*)
dir_to_scan="$1"
shift
;;
esac
done

if [[ $# -gt 0 ]]; then
dir_to_scan="$1"
fi

if [[ -z "$scan_mode" ]]; then
echo "Error: --mode must be provided (iac|package)." >&2
usage
exit 1
fi

case "$scan_mode" in
iac|package)
;;
*)
echo "Error: unknown mode '$scan_mode'. Expected 'iac' or 'package'." >&2
usage
exit 1
;;
esac

if command -v trivy > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then
run-trivy-natively "$scan_mode" "$dir_to_scan"
else
run-trivy-in-docker "$scan_mode" "$dir_to_scan"
fi
}

function run-trivy-natively() {
local scan_mode="$1"
local dir_to_scan="$2"

echo "Trivy found locally, running natively"
echo "Running Trivy ($scan_mode) on directory: $dir_to_scan"

if execute-trivy-command "$scan_mode" "$dir_to_scan"; then
check-trivy-status 0
else
local status=$?
check-trivy-status "$status"
fi
}

function run-trivy-in-docker() {
# shellcheck disable=SC1091
source ./scripts/docker/docker.lib.sh

local scan_mode="$1"
local dir_to_scan="$2"

# shellcheck disable=SC2155
local image=$(name=aquasec/trivy docker-get-image-version-and-pull)

echo "Trivy not found locally, running in Docker Container"
echo "Running Trivy ($scan_mode) on directory: $dir_to_scan"

if execute-trivy-in-docker "$image" "$scan_mode" "$dir_to_scan"; then
check-trivy-status 0
else
local status=$?
check-trivy-status "$status"
fi
}

function execute-trivy-command() {
local scan_mode="$1"
local dir_to_scan="$2"

if [[ "$scan_mode" == "iac" ]]; then
trivy config \
--config scripts/config/trivy.yaml \
--tf-exclude-downloaded-modules \
"$dir_to_scan"
else
trivy \
--config scripts/config/trivy.yaml \
fs "$dir_to_scan" \
--scanners vuln \
--severity HIGH,CRITICAL \
--include-dev-deps
fi
}

function execute-trivy-in-docker() {
local image="$1"
local scan_mode="$2"
local dir_to_scan="$3"

if [[ "$scan_mode" == "iac" ]]; then
docker run --rm --platform linux/amd64 \
--volume "$PWD":/workdir \
--workdir /workdir \
"$image" \
config \
--config scripts/config/trivy.yaml \
--tf-exclude-downloaded-modules \
"$dir_to_scan"
else
docker run --rm --platform linux/amd64 \
--volume "$PWD":/workdir \
--workdir /workdir \
"$image" \
--config scripts/config/trivy.yaml \
fs "$dir_to_scan" \
--scanners vuln \
--severity HIGH,CRITICAL \
--include-dev-deps
fi
}

function check-trivy-status() {
local status="$1"

if [[ "$status" -eq 0 ]]; then
echo "Trivy completed successfully."
return 0
fi

echo "Trivy found issues."
exit "$status"
}

function is-arg-true() {
if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then
return 0
else
return 1
fi
}

# ==============================================================================

is-arg-true "${VERBOSE:-false}" && set -x

main "$@"

exit 0
Loading
Loading