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
22 changes: 7 additions & 15 deletions .github/workflows/build_all_images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ jobs:

build_tool_images:
# build common tool images with a lower scoped github token
# as it uses a 3rd party docker image with github cli installed to verify attestation of tflint binary
# and we dont want to make a high scoped token available to that image
# token needs attestation read so it can verify attestation of tflint binary
# as it uses a 3rd party docker image with github cli installed to verify attestation of binaries downloaded from github
# and we don't want to make a high scoped token available to that image
# token needs attestation read so it can verify attestation of binaries
name: Build tool images for on ${{ matrix.arch }}
runs-on: '${{ matrix.runner }}'
strategy:
Expand All @@ -59,23 +59,14 @@ jobs:
with:
fetch-depth: 0
persist-credentials: false
- name: build_grype
- name: build_tools
run: |
make build-grype
make build-tools
docker save "local_grype:latest" -o grype_image.tar
- name: build_syft
run: |
make build-syft
docker save "local_syft:latest" -o syft_image.tar
- name: build_grant
run: |
make build-grant
docker save "local_grant:latest" -o grant_image.tar

- name: build_tflint
run: |
make build-tflint
docker save "local_tflint:latest" -o tflint_image.tar
docker save "local_zizmor:latest" -o zizmor_image.tar
Comment thread
anthony-nhs marked this conversation as resolved.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand All @@ -88,6 +79,7 @@ jobs:
syft_image.tar
grant_image.tar
tflint_image.tar
zizmor_image.tar
package_base_docker_image:
uses: ./.github/workflows/build_multi_arch_image.yml
permissions:
Expand Down
2 changes: 2 additions & 0 deletions .grype.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ ignore:
- vulnerability: CVE-2026-32283
- vulnerability: CVE-2026-32281
- vulnerability: CVE-2026-33810
- vulnerability: CVE-2026-6100
- vulnerability: CVE-2026-4786
# node_24 vulnerabilities
- vulnerability: GHSA-c2c7-rcm5-vvqj
- vulnerability: GHSA-7r86-cg39-jmmj
Expand Down
41 changes: 37 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ guard-%:
.PHONY: install install-python install-node install-hooks build-base-image build-node-24-image build-node-24-python-3-10-image build-node-24-python-3-12-image build-node-24-python-3-13-image build-node-24-python-3-14-image \
build-eps-storage-terraform-image build-eps-data-extract-image build-fhir-facade-image build-node-24-python-3-14-golang-1-24-image build-node-24-python-3-14-java-24-image \
build-regression-tests-image build-all build-image build-githubactions-image scan-image scan-image-json shell-image lint test lint-githubactions lint-githubaction-scripts clean \
build-syft build-grype build-grant build-tflint
build-syft build-grype build-grant build-tflint build-tools build-zizmor
install: install-python install-node install-hooks

install-python:
Expand Down Expand Up @@ -89,14 +89,35 @@ build-tflint:
@if docker image inspect local_tflint:latest >/dev/null 2>&1; then \
echo "Image local_tflint:latest already exists. Skipping build."; \
else \
if [ -z "$$GITHUB_TOKEN" ]; then \
echo "GITHUB_TOKEN environment variable not set. Please set it by running 'make github-login' and setting GITHUB_TOKEN to the value of 'gh auth token'."; \
exit 1; \
fi; \
docker buildx build \
--secret id=GH_TOKEN,env=GITHUB_TOKEN \
-f src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint \
-f src/base/.devcontainer/Dockerfile.tflint \
--tag local_tflint:latest \
src/projects/eps-storage-terraform/.devcontainer/; \
src/base/.devcontainer/; \
Comment on lines 96 to +100
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker buildx build doesn’t guarantee the image will be loaded into the local Docker image store unless --load (or --output type=docker) is used. The workflow immediately runs docker save local_tflint:latest, so this can fail depending on the active buildx driver/builder. Add --load (or switch to docker build if secrets aren’t needed) to make the target deterministic.

Copilot uses AI. Check for mistakes.
fi

build-image: build-syft build-grype build-grant build-tflint guard-CONTAINER_NAME guard-BASE_VERSION_TAG guard-BASE_FOLDER guard-IMAGE_TAG
build-zizmor:
@if docker image inspect local_zizmor:latest >/dev/null 2>&1; then \
echo "Image local_zizmor:latest already exists. Skipping build."; \
else \
if [ -z "$$GITHUB_TOKEN" ]; then \
echo "GITHUB_TOKEN environment variable not set. Please set it by running 'make github-login' and setting GITHUB_TOKEN to the value of 'gh auth token'."; \
exit 1; \
fi; \
docker buildx build \
--secret id=GH_TOKEN,env=GITHUB_TOKEN \
-f src/base/.devcontainer/Dockerfile.zizmor \
--tag local_zizmor:latest \
src/base/.devcontainer/; \
Comment on lines +111 to +115
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as build-tflint: docker buildx build may not load local_zizmor:latest into the local image store without --load / --output type=docker. Since CI later does docker save local_zizmor:latest, this can break depending on the buildx driver. Add --load (or explicit output) so the image is guaranteed to exist locally.

Copilot uses AI. Check for mistakes.
fi

build-tools: build-syft build-grype build-grant build-tflint build-zizmor

build-image: build-tools guard-CONTAINER_NAME guard-BASE_VERSION_TAG guard-BASE_FOLDER guard-IMAGE_TAG
workspace_folder="$${CONTAINER_NAME}"; \
case "$${CONTAINER_NAME}" in \
eps_*) workspace_folder="$$(printf '%s' "$${CONTAINER_NAME}" | tr '_' '-')" ;; \
Expand Down Expand Up @@ -149,6 +170,18 @@ lint-githubaction-scripts:

clean:
rm -rf .out
docker image rm local_syft:latest || true
docker image rm local_grype:latest || true
docker image rm local_grant:latest || true
docker image rm local_tflint:latest || true
docker image rm local_zizmor:latest || true

deep-clean: clean
rm -rf .venv
find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +
poetry env remove --all
docker images --format "{{.Repository}}:{{.Tag}}" | grep ":local-build" | xargs -r docker rmi -f


%:
@$(MAKE) -f /usr/local/share/eps/Mk/common.mk $@
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ The base image contains
- asdf
- aws cli
- aws sam cli
- gitleaks
- shellcheck
- direnv
- yq
- zizmor
- grype
- syft
- grant

It installs the following dev container features
- docker outside of docker
Expand All @@ -41,14 +49,10 @@ The base image contains
As the vscode user the following also happens

asdf install and setup for these so they are available globally as vscode user
- shellcheck
- direnv
- actionlint
- ruby (for GitHub Pages)
- yq

Install and setup git-secrets.
Install [zizmor](https://github.com/zizmorcore/zizmor).

# Using the images
## Project setup
Expand Down Expand Up @@ -176,7 +180,7 @@ Check targets (`check.mk`)
- `cfn-guard-cdk` - validates `cdk.out` against cfn-guard rulesets and writes outputs to `.cfn_guard_out/`
- `cfn-guard-terraform` - validates `terraform_plans` against cfn-guard rulesets and writes outputs to `.cfn_guard_out/`
- `actionlint` - runs actionlint against GitHub Actions
- `secret-scan` - runs git-secrets (including scanning history) against the repository
- `secret-scan` - runs git-secrets or gitleaks (including scanning history) against the repository
- `guard-<ENVIRONMENT_VARIABLE>` - checks if an environment variable is set and errors if it is not
- `zizmor` - runs [zizmor](https://github.com/zizmorcore/zizmor) in the local directory to check github workflows and actions
- `syft-generate-sbom` - uses syft to generate an sbom in cyclonedx-json format. This *does not* include dev dependencies. Outputs file to .sbom/sbom.cdx.json.
Expand Down Expand Up @@ -369,7 +373,7 @@ CONTAINER_NAME=base \

# Cleaning up unused container images

There is a script to delete unused container images. This runs on every merge to main and deletes pull request images, and on a weekly schedule it deletes images created by CI.
There is a script to delete unused container images on GitHub. This runs on every merge to main and deletes pull request images, and on a weekly schedule it deletes images created by CI.
You can run it manually using the following. Using the `dry-run` flag just shows what would be deleted

```
Expand Down
3 changes: 0 additions & 3 deletions src/base/.devcontainer/.tool-versions
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
shellcheck 0.11.0
direnv 2.37.1
actionlint 1.7.12
ruby 3.3.0
yq 4.52.5
2 changes: 0 additions & 2 deletions src/base/.devcontainer/.tool-versions.asdf

This file was deleted.

26 changes: 23 additions & 3 deletions src/base/.devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
FROM local_syft:latest AS syft-build
FROM local_grype:latest AS grype-build
FROM local_grant:latest AS grant-build
FROM local_zizmor:latest AS zizmor-build
FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04

ARG SCRIPTS_DIR=/usr/local/share/eps
ARG CONTAINER_NAME
ARG IMAGE_TAG
ARG TARGETARCH
ARG SAM_VERSION="v1.158.0"
ARG ASDF_VERSION="v0.18.1"
ARG GITLEAKS_VERSION="8.30.1"
ARG CFN_GUARD_VERSION="3.2.0"
ARG SHELLCHECK_VERSION="v0.11.0"
ARG DIRENV_VERSION="v2.37.1"
ARG YQ_VERSION="v4.52.5"

ENV SCRIPTS_DIR=${SCRIPTS_DIR}
ENV CONTAINER_NAME=${CONTAINER_NAME}
ENV TARGETARCH=${TARGETARCH}
ENV SAM_VERSION=${SAM_VERSION}
ENV ASDF_VERSION=${ASDF_VERSION}
ENV GITLEAKS_VERSION=${GITLEAKS_VERSION}
ENV CFN_GUARD_VERSION=${CFN_GUARD_VERSION}
ENV SHELLCHECK_VERSION=${SHELLCHECK_VERSION}
ENV DIRENV_VERSION=${DIRENV_VERSION}
ENV YQ_VERSION=${YQ_VERSION}

COPY .tool-versions.asdf ${SCRIPTS_DIR}/${CONTAINER_NAME}/.tool-versions.asdf
COPY --chmod=755 scripts/lifecycle/*.sh ${SCRIPTS_DIR}/
COPY --chmod=755 scripts/root_install.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/root_install.sh
COPY --chmod=755 scripts/install_aws_sam_cli.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/install_aws_sam_cli.sh
COPY --chmod=755 scripts/install_asdf.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/install_asdf.sh
COPY --chmod=755 scripts/install_gitleaks.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/install_gitleaks.sh
COPY --chmod=755 scripts/install_cfn_guard.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/install_cfn_guard.sh
COPY --chmod=755 scripts/install_shellcheck.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/install_shellcheck.sh
COPY --chmod=755 scripts/install_direnv.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/install_direnv.sh
COPY --chmod=755 scripts/install_yq.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/install_yq.sh
COPY --chmod=755 Mk ${SCRIPTS_DIR}/Mk

WORKDIR ${SCRIPTS_DIR}/${CONTAINER_NAME}
Expand All @@ -23,10 +44,9 @@ RUN ./root_install.sh
COPY --from=syft-build /syft /usr/local/bin/syft
COPY --from=grype-build /grype /usr/local/bin/grype
COPY --from=grant-build /grant /usr/local/bin/grant

COPY --from=zizmor-build /zizmor /usr/local/bin/zizmor
COPY --chmod=755 scripts/vscode_install.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/vscode_install.sh
USER vscode
COPY --chown=vscode:vscode .tool-versions.asdf /home/vscode/.tool-versions.asdf
COPY --chown=vscode:vscode .tool-versions /home/vscode/.tool-versions
COPY --chown=vscode:vscode .grant.yaml /home/vscode/.grant.yaml

Expand Down
24 changes: 24 additions & 0 deletions src/base/.devcontainer/Dockerfile.tflint
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM serversideup/github-cli:2.89.0 AS build
ARG TARGETARCH
ARG TFLINT_VERSION="v0.61.0"
COPY --chmod=755 scripts/install_github_release.sh /tmp/install_github_release.sh
RUN --mount=type=secret,id=GH_TOKEN,env=GH_TOKEN \
case "${TARGETARCH}" in \
x86_64|amd64) DOWNLOAD_BINARY=tflint_linux_amd64.zip ;; \
aarch64|arm64) DOWNLOAD_BINARY=tflint_linux_arm64.zip ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \
esac \
&& INSTALL_DIR=/tmp/tflint/ \
ARCH="${TARGETARCH}" \
VERSION="${TFLINT_VERSION}" \
GITHUB_REPO="terraform-linters/tflint" \
TOOL="tflint" \
DOWNLOAD_BINARY="${DOWNLOAD_BINARY}" \
VERIFY_BINARY_ATTESTATION="false" \
VERIFY_CHECKSUM="true" \
COMPRESSION="zip" \
/tmp/install_github_release.sh

FROM scratch
COPY --from=build /tmp/tflint/tflint /tflint
ENTRYPOINT ["/tflint"]
24 changes: 24 additions & 0 deletions src/base/.devcontainer/Dockerfile.zizmor
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM serversideup/github-cli:2.89.0 AS build
ARG TARGETARCH
ARG ZIZMOR_VERSION="v1.24.1"
COPY --chmod=755 scripts/install_github_release.sh /tmp/install_github_release.sh
RUN --mount=type=secret,id=GH_TOKEN,env=GH_TOKEN \
case "${TARGETARCH}" in \
x86_64|amd64) DOWNLOAD_BINARY=zizmor-x86_64-unknown-linux-gnu.tar.gz ;; \
aarch64|arm64) DOWNLOAD_BINARY=zizmor-aarch64-unknown-linux-gnu.tar.gz ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \
esac \
&& INSTALL_DIR=/tmp/zizmor/ \
ARCH="${TARGETARCH}" \
VERSION="${ZIZMOR_VERSION}" \
GITHUB_REPO="zizmorcore/zizmor" \
TOOL="zizmor" \
DOWNLOAD_BINARY="${DOWNLOAD_BINARY}" \
VERIFY_BINARY_ATTESTATION="true" \
VERIFY_CHECKSUM="false" \
COMPRESSION="tar.gz" \
/tmp/install_github_release.sh

FROM scratch
COPY --from=build /tmp/zizmor/zizmor /zizmor
ENTRYPOINT ["/zizmor"]
6 changes: 5 additions & 1 deletion src/base/.devcontainer/Mk/check.mk
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ actionlint:
actionlint

secret-scan:
git-secrets --scan-history .
@if [ -f .gitallowed ]; then \
git-secrets --scan-history .; \
else \
gitleaks -v --redact git; \
Comment thread
anthony-nhs marked this conversation as resolved.
fi

guard-%:
@ if [ "${${*}}" = "" ]; then \
Expand Down
2 changes: 2 additions & 0 deletions src/base/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"moby": "true",
"installDockerBuildx": "true"
},
// fine to use github-cli feature here as it verifies the installation
"ghcr.io/devcontainers/features/github-cli:1": {},
// fine to use aws-cli feature here as it verifies the installation
"ghcr.io/devcontainers/features/aws-cli:1": {
"version": "latest"
}
Expand Down
56 changes: 56 additions & 0 deletions src/base/.devcontainer/scripts/install_asdf.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail

VERSION=${VERSION:-"v0.18.1"}
# Expected SHA256 checksums taken from https://github.com/asdf-vm/asdf/releases/tag/v0.18.1
# When we change asdf versions, these must be changed
sha256sum_expected_arm="sha256:1850faf576cab7acb321e99dd98d3fe0d4665e1331086ad9ed991aeec1dc9d36"
sha256sum_expected_amd64="sha256:56141dc99eab75c140dcdd85cf73f3b82fed2485a8dccd4f11a4dc5cbcb6ea5c"

if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi

# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
apt-get -y install --no-install-recommends "$@"
fi
}

check_packages curl ca-certificates tar

install() {
tmp_dir="$(mktemp -d)"
trap 'rm -rf "${tmp_dir}"' EXIT

download_file="${tmp_dir}/asdf.tar.gz"

if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" == "aarch64" ]; then
download_url="https://github.com/asdf-vm/asdf/releases/download/${VERSION}/asdf-${VERSION}-linux-arm64.tar.gz"
sha256sum_expected="${sha256sum_expected_arm}"
else
download_url="https://github.com/asdf-vm/asdf/releases/download/${VERSION}/asdf-${VERSION}-linux-amd64.tar.gz"
sha256sum_expected="${sha256sum_expected_amd64}"
fi
curl -fsSL "${download_url}" -o "${download_file}"

download_file_sha256sum=$(sha256sum "${download_file}" | awk '{print $1}')
if [ "${download_file_sha256sum}" != "${sha256sum_expected#sha256:}" ]; then
echo "SHA256 checksum mismatch for downloaded asdf archive"
echo "Expected: ${sha256sum_expected}"
echo "Actual: sha256:${download_file_sha256sum}"
exit 1
fi

tar -xzf "${download_file}" -C "${tmp_dir}"
mkdir -p /usr/bin
mv "${tmp_dir}/asdf" /usr/bin/asdf
chmod +x /usr/bin/asdf
}
echo "(*) Installing asdf..."

install

echo "Done!"
Loading