Conversation
…ithub_actions_use_sha
|
This PR is linked to a ticket in an NHS Digital JIRA Project. Here's a handy link to the ticket: AEA-0000 |
There was a problem hiding this comment.
Pull request overview
Replaces the Trivy-based image scanning and ignore-file workflow with Anchore tooling (Syft/Grype), updates CI to install/run the new scanners, and removes Trivy ignore/config artifacts across images.
Changes:
- Remove Trivy config/ignore files and Trivy helper tooling/docs; disable Trivy make targets.
- Add verified install flow for Anchore tools (Syft/Grype) and wire them into the base devcontainer + CI workflow.
- Add
zizmor(andcfn-lintfor some images) via per-imagerequirements-user.txt, and expose amake zizmortarget.
Reviewed changes
Copilot reviewed 49 out of 49 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| trivy.yaml | Removes repository-level Trivy ignorefile config. |
| src/projects/regression_tests/trivy.yaml | Removes project Trivy ignorefile config. |
| src/projects/regression_tests/.trivyignore.yaml | Removes project Trivy ignore list. |
| src/projects/node_24_python_3_14_java_24/trivy.yaml | Removes project Trivy ignorefile config. |
| src/projects/node_24_python_3_14_java_24/.trivyignore.yaml | Removes project Trivy ignore list. |
| src/projects/node_24_python_3_14_golang_1_24/trivy.yaml | Removes project Trivy ignorefile config. |
| src/projects/node_24_python_3_14_golang_1_24/.trivyignore.yaml | Removes project Trivy ignore list. |
| src/projects/fhir_facade_api/trivy.yaml | Removes project Trivy ignorefile config. |
| src/projects/fhir_facade_api/.trivyignore.yaml | Removes project Trivy ignore list. |
| src/projects/fhir_facade_api/.devcontainer/.tool-versions | Pins Java to a specific patch version. |
| src/projects/eps-storage-terraform/trivy.yaml | Removes project Trivy ignorefile config. |
| src/projects/eps-storage-terraform/.trivyignore.yaml | Removes project Trivy ignore list. |
| src/languages/node_24_python_3_14/trivy.yaml | Removes language image Trivy ignorefile config. |
| src/languages/node_24_python_3_14/.trivyignore.yaml | Removes language image Trivy ignore list. |
| src/languages/node_24_python_3_14/.devcontainer/scripts/vscode_install.sh | Switches to installing user pip tools from a requirements file. |
| src/languages/node_24_python_3_14/.devcontainer/scripts/requirements-user.txt | Adds pinned zizmor and cfn-lint. |
| src/languages/node_24_python_3_13/trivy.yaml | Removes language image Trivy ignorefile config. |
| src/languages/node_24_python_3_13/.trivyignore.yaml | Removes language image Trivy ignore list marker. |
| src/languages/node_24_python_3_13/.devcontainer/scripts/vscode_install.sh | Switches to installing user pip tools from a requirements file. |
| src/languages/node_24_python_3_13/.devcontainer/scripts/requirements-user.txt | Adds pinned zizmor and cfn-lint. |
| src/languages/node_24_python_3_12/trivy.yaml | Removes language image Trivy ignorefile config. |
| src/languages/node_24_python_3_12/.trivyignore.yaml | Removes language image Trivy ignore list marker. |
| src/languages/node_24_python_3_12/.devcontainer/scripts/vscode_install.sh | Switches to installing user pip tools from a requirements file. |
| src/languages/node_24_python_3_12/.devcontainer/scripts/requirements-user.txt | Adds pinned zizmor and cfn-lint. |
| src/languages/node_24_python_3_10/trivy.yaml | Removes language image Trivy ignorefile config. |
| src/languages/node_24_python_3_10/.trivyignore.yaml | Removes language image Trivy ignore list. |
| src/languages/node_24_python_3_10/.devcontainer/scripts/vscode_install.sh | Adds requirements-based pip installs. |
| src/languages/node_24_python_3_10/.devcontainer/scripts/requirements-user.txt | Adds pinned zizmor. |
| src/common_node_24/Dockerfile | Attempts to copy requirements-user.txt into images built from this common Dockerfile. |
| src/common/.trivyignore.yaml | Removes common/base Trivy ignore list. |
| src/base_node/node_24/trivy.yaml | Removes base-node Trivy ignorefile config. |
| src/base/trivy.yaml | Removes base image Trivy ignorefile config. |
| src/base/.trivyignore.yaml | Removes base image Trivy ignore list marker. |
| src/base/.devcontainer/scripts/vscode_install.sh | Removes ASDF Trivy plugin installation. |
| src/base/.devcontainer/scripts/install_trivy.sh | Removes Trivy install script. |
| src/base/.devcontainer/scripts/install_anchore_tool.sh | Adds Syft/Grype installer with signature/checksum verification. |
| src/base/.devcontainer/Mk/trivy.mk | Strips out old commented Trivy target implementations (keeps “Not implemented”). |
| src/base/.devcontainer/Mk/check.mk | Adds zizmor make target. |
| src/base/.devcontainer/Dockerfile.trivy.arm64 | Removes Trivy tool image definition. |
| src/base/.devcontainer/Dockerfile.trivy.amd64 | Removes Trivy tool image definition. |
| src/base/.devcontainer/Dockerfile.syft | Adds Syft tool image definition. |
| src/base/.devcontainer/Dockerfile.grype | Adds Grype tool image definition. |
| src/base/.devcontainer/Dockerfile | Injects Syft/Grype binaries into the base devcontainer image; updates PATH. |
| scripts/trivy_to_trivyignore.py | Removes helper for generating .trivyignore from Trivy JSON output. |
| README.md | Removes Trivy docs; documents zizmor; notes Trivy targets will be removed later. |
| Makefile | Adds build steps for Syft/Grype tool images; replaces Trivy scan targets with Grype scan targets. |
| .trivyignore.yaml | Removes repository-level Trivy ignore list. |
| .github/workflows/build_multi_arch_image.yml | Replaces Trivy setup/scan with Syft/Grype setup + Grype scan + artifact upload. |
| .devcontainer/Dockerfile.bootstrap | Removes Trivy bootstrap installation steps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # use glob pattern to copy requirements-user.txt if it exists, to avoid build failure if it doesn't | ||
| COPY scripts/requirements-user.tx[t] ${SCRIPTS_DIR}/${CONTAINER_NAME}/requirements-user.txt |
There was a problem hiding this comment.
COPY scripts/requirements-user.tx[t] ... does not make the copy optional: Docker expands source globs and the build fails if the pattern matches no files. If the intent is “copy if present”, either ensure scripts/requirements-user.txt always exists in the build context (possibly empty), or handle optional install in vscode_install.sh by checking for the file before running pip install -r.
| # use glob pattern to copy requirements-user.txt if it exists, to avoid build failure if it doesn't | |
| COPY scripts/requirements-user.tx[t] ${SCRIPTS_DIR}/${CONTAINER_NAME}/requirements-user.txt | |
| # copy requirements-user.txt into the container; ensure this file exists in the build context (it may be empty) | |
| COPY scripts/requirements-user.txt ${SCRIPTS_DIR}/${CONTAINER_NAME}/requirements-user.txt |
| grype "${CONTAINER_PREFIX}$${CONTAINER_NAME}:$${IMAGE_TAG}" \ | ||
| --scope all-layers \ | ||
| --output json \ | ||
| --file ".grype_out/grype_${CONTAINER_NAME}_${IMAGE_TAG}.json" \ | ||
| --sort-by severity |
There was a problem hiding this comment.
scan-image-json writes to .grype_out/... but the target doesn’t create the .grype_out directory, so this will fail on a clean checkout. Add a mkdir -p .grype_out before invoking grype (and consider also writing a text/table output to a file for later inspection in CI).
| name: "grype_${{ inputs.container_name }}_${{ matrix.arch }}.json" | ||
| path: .grype_out/grype_${{ inputs.container_name }}_${{ matrix.arch }}.json |
There was a problem hiding this comment.
The filename produced by scan-image-json is ./.grype_out/grype_${CONTAINER_NAME}_${IMAGE_TAG}.json, but the workflow later uploads .grype_out/grype_${container_name}_${matrix.arch}.json. With IMAGE_TAG set to ${docker_tag}-${arch}, these paths won’t match, so the upload step will fail. Align the Makefile output name with the workflow expectation (or update the workflow to use the Makefile naming).
| name: "grype_${{ inputs.container_name }}_${{ matrix.arch }}.json" | |
| path: .grype_out/grype_${{ inputs.container_name }}_${{ matrix.arch }}.json | |
| name: "grype_${{ inputs.container_name }}_${{ inputs.docker_tag }}-${{ matrix.arch }}.json" | |
| path: .grype_out/grype_${{ inputs.container_name }}_${{ inputs.docker_tag }}-${{ matrix.arch }}.json |
| - name: Show docker vulnerability output | ||
| if: always() | ||
| run: | | ||
| echo "Scan output for ghcr.io/nhsdigital/eps-devcontainers/base:${DOCKER_TAG}-${ARCHITECTURE}" | ||
| if [ -f .out/scan_results_docker.txt ]; then | ||
| cat .out/scan_results_docker.txt | ||
| fi | ||
| env: | ||
| ARCHITECTURE: '${{ matrix.arch }}' | ||
| DOCKER_TAG: '${{ inputs.docker_tag }}' |
There was a problem hiding this comment.
The “Show docker vulnerability output” step still checks for .out/scan_results_docker.txt, but make scan-image no longer writes anything to .out/. As written, this step will always print nothing even if the scan finds issues. Either capture grype output to a file (e.g., under .grype_out/) and update this step to cat that file, or remove the step.
| - name: Show docker vulnerability output | |
| if: always() | |
| run: | | |
| echo "Scan output for ghcr.io/nhsdigital/eps-devcontainers/base:${DOCKER_TAG}-${ARCHITECTURE}" | |
| if [ -f .out/scan_results_docker.txt ]; then | |
| cat .out/scan_results_docker.txt | |
| fi | |
| env: | |
| ARCHITECTURE: '${{ matrix.arch }}' | |
| DOCKER_TAG: '${{ inputs.docker_tag }}' |
| scan-image: guard-CONTAINER_NAME guard-BASE_FOLDER guard-IMAGE_TAG | ||
| grype "${CONTAINER_PREFIX}$${CONTAINER_NAME}:$${IMAGE_TAG}" \ | ||
| --scope all-layers \ | ||
| --sort-by severity |
There was a problem hiding this comment.
scan-image currently runs grype without any explicit failure threshold/severity policy (previous Trivy command gated on HIGH/CRITICAL with --exit-code 1). If this target is meant to enforce vulnerability gating in CI, add the appropriate Grype options (e.g., --fail-on / severity configuration) so the build fails when policy is violated.
| @@ -142,7 +119,6 @@ lint-githubaction-scripts: | |||
|
|
|||
| clean: | |||
| rm -rf .out | |||
There was a problem hiding this comment.
clean removes .out but the new scan artifacts are written under .grype_out. Update clean to remove .grype_out as well, otherwise local runs will accumulate stale Grype outputs and CI reruns may upload outdated files if names collide.
| rm -rf .out | |
| rm -rf .out .grype_out |
| BASE_URL="https://github.com/anchore/${TOOL}/releases/download/v${VERSION}" | ||
| ARCHIVE="${TOOL}_${VERSION}_linux_${ARCH}.tar.gz" | ||
| CHECKSUMS="${TOOL}_${VERSION}_checksums.txt" | ||
| CHECKSUMS_PEM="${TOOL}_${VERSION}_checksums.txt.pem" | ||
| CHECKSUMS_SIG="${TOOL}_${VERSION}_checksums.txt.sig" | ||
|
|
||
| if [ -z "$TOOL" ] | ||
| then | ||
| echo "\$TOOL is NULL" | ||
| fi | ||
| if [ -z "$ARCH" ] | ||
| then | ||
| echo "\$ARCH is NULL" | ||
| fi | ||
| if [ -z "$VERSION" ] | ||
| then | ||
| echo "\$VERSION is NULL" | ||
| fi | ||
|
|
There was a problem hiding this comment.
set -euo pipefail means referencing an unset variable aborts the script, but BASE_URL, ARCHIVE, etc. are constructed using ${TOOL}, ${VERSION}, ${ARCH} before you validate them. This will fail with an “unbound variable” error when any are missing, and the later -z checks won’t run. Move validation to the top using ${TOOL:?}, ${VERSION:?}, ${ARCH:?} (or use ${TOOL:-} in checks) and exit non-zero with a clear message when required vars are not set.
| BASE_URL="https://github.com/anchore/${TOOL}/releases/download/v${VERSION}" | |
| ARCHIVE="${TOOL}_${VERSION}_linux_${ARCH}.tar.gz" | |
| CHECKSUMS="${TOOL}_${VERSION}_checksums.txt" | |
| CHECKSUMS_PEM="${TOOL}_${VERSION}_checksums.txt.pem" | |
| CHECKSUMS_SIG="${TOOL}_${VERSION}_checksums.txt.sig" | |
| if [ -z "$TOOL" ] | |
| then | |
| echo "\$TOOL is NULL" | |
| fi | |
| if [ -z "$ARCH" ] | |
| then | |
| echo "\$ARCH is NULL" | |
| fi | |
| if [ -z "$VERSION" ] | |
| then | |
| echo "\$VERSION is NULL" | |
| fi | |
| # Validate required environment variables before first use. | |
| : "${TOOL:?Environment variable TOOL must be set (e.g., 'syft' or 'grype')}" | |
| : "${VERSION:?Environment variable VERSION must be set (e.g., '1.0.0')}" | |
| : "${ARCH:?Environment variable ARCH must be set (e.g., 'amd64')}" | |
| BASE_URL="https://github.com/anchore/${TOOL}/releases/download/v${VERSION}" | |
| ARCHIVE="${TOOL}_${VERSION}_linux_${ARCH}.tar.gz" | |
| CHECKSUMS="${TOOL}_${VERSION}_checksums.txt" | |
| CHECKSUMS_PEM="${TOOL}_${VERSION}_checksums.txt.pem" | |
| CHECKSUMS_SIG="${TOOL}_${VERSION}_checksums.txt.sig" |
| if [ -z "$TOOL" ] | ||
| then | ||
| echo "\$TOOL is NULL" | ||
| fi | ||
| if [ -z "$ARCH" ] | ||
| then | ||
| echo "\$ARCH is NULL" | ||
| fi | ||
| if [ -z "$VERSION" ] | ||
| then | ||
| echo "\$VERSION is NULL" | ||
| fi |
There was a problem hiding this comment.
The if [ -z "$TOOL" ] ... echo ... fi blocks don’t stop execution, so the script will continue (and later curl/cosign/tar/install) even when required inputs are missing. After validating inputs, exit 1 on missing/invalid values (and ideally restrict TOOL to syft|grype to avoid unexpected downloads).
| fi | ||
|
|
||
| zizmor: | ||
| zizmor . |
There was a problem hiding this comment.
The new zizmor target assumes zizmor is installed in every devcontainer, but the base image’s vscode_install.sh doesn’t install it (and only the Node+Python language images add it via pip install --user). Either install zizmor in the base image so the target works everywhere, or make the target conditional with a clear error message when zizmor isn’t available.
| zizmor . | |
| @ if command -v zizmor >/dev/null 2>&1; then \ | |
| zizmor .; \ | |
| else \ | |
| echo "Error: 'zizmor' is not installed in this devcontainer. Install zizmor or use a language image that provides it." >&2; \ | |
| exit 1; \ | |
| fi |
Summary
Details