diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index 98484a0c..a56ce74c 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -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: @@ -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 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -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: diff --git a/.grype.yaml b/.grype.yaml index 8a3d028f..df239c56 100644 --- a/.grype.yaml +++ b/.grype.yaml @@ -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 diff --git a/Makefile b/Makefile index 140fdba4..f46a6199 100644 --- a/Makefile +++ b/Makefile @@ -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: @@ -91,12 +91,25 @@ build-tflint: else \ 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/; \ 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 \ + docker buildx build \ + --secret id=GH_TOKEN,env=GITHUB_TOKEN \ + -f src/base/.devcontainer/Dockerfile.zizmor \ + --tag local_zizmor:latest \ + src/base/.devcontainer/; \ + 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 '_' '-')" ;; \ diff --git a/src/base/.devcontainer/.tool-versions.asdf b/src/base/.devcontainer/.tool-versions.asdf deleted file mode 100644 index 9ee20c51..00000000 --- a/src/base/.devcontainer/.tool-versions.asdf +++ /dev/null @@ -1,2 +0,0 @@ -# define the .asdf-version to use here -0.18.1 diff --git a/src/base/.devcontainer/Dockerfile b/src/base/.devcontainer/Dockerfile index fe46e449..7650b81d 100644 --- a/src/base/.devcontainer/Dockerfile +++ b/src/base/.devcontainer/Dockerfile @@ -1,20 +1,31 @@ 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" ENV SCRIPTS_DIR=${SCRIPTS_DIR} ENV CONTAINER_NAME=${CONTAINER_NAME} ENV TARGETARCH=${TARGETARCH} - -COPY .tool-versions.asdf ${SCRIPTS_DIR}/${CONTAINER_NAME}/.tool-versions.asdf +ENV SAM_VERSION=${SAM_VERSION} +ENV ASDF_VERSION=${ASDF_VERSION} +ENV GITLEAKS_VERSION=${GITLEAKS_VERSION} +ENV CFN_GUARD_VERSION=${CFN_GUARD_VERSION} 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 Mk ${SCRIPTS_DIR}/Mk WORKDIR ${SCRIPTS_DIR}/${CONTAINER_NAME} @@ -23,10 +34,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 diff --git a/src/base/.devcontainer/Dockerfile.tflint b/src/base/.devcontainer/Dockerfile.tflint new file mode 100644 index 00000000..4e61f773 --- /dev/null +++ b/src/base/.devcontainer/Dockerfile.tflint @@ -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"] diff --git a/src/base/.devcontainer/Dockerfile.zizmor b/src/base/.devcontainer/Dockerfile.zizmor new file mode 100644 index 00000000..de563c90 --- /dev/null +++ b/src/base/.devcontainer/Dockerfile.zizmor @@ -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"] diff --git a/src/base/.devcontainer/Mk/check.mk b/src/base/.devcontainer/Mk/check.mk index 2bf0a03f..73a7b0da 100644 --- a/src/base/.devcontainer/Mk/check.mk +++ b/src/base/.devcontainer/Mk/check.mk @@ -84,7 +84,11 @@ actionlint: actionlint secret-scan: - git-secrets --scan-history . + @if [ -f .gitallowed ]; then \ + git-secrets --scan-history .; \ + else \ + gitleaks -v git; \ + fi guard-%: @ if [ "${${*}}" = "" ]; then \ diff --git a/src/base/.devcontainer/devcontainer.json b/src/base/.devcontainer/devcontainer.json index 9b8e753c..a27fa6ee 100644 --- a/src/base/.devcontainer/devcontainer.json +++ b/src/base/.devcontainer/devcontainer.json @@ -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" } diff --git a/src/base/.devcontainer/scripts/install_asdf.sh b/src/base/.devcontainer/scripts/install_asdf.sh new file mode 100755 index 00000000..abad77fd --- /dev/null +++ b/src/base/.devcontainer/scripts/install_asdf.sh @@ -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!" diff --git a/src/base/.devcontainer/scripts/install_aws_sam_cli.sh b/src/base/.devcontainer/scripts/install_aws_sam_cli.sh new file mode 100755 index 00000000..47b3a053 --- /dev/null +++ b/src/base/.devcontainer/scripts/install_aws_sam_cli.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "${VERSION}" = "latest" ]; then + VERSION="$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/aws/aws-sam-cli/releases/latest | awk -F/ '{print $NF}')" +fi + +PRIMARY_PUBLIC_KEY="-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2.0.22 (GNU/Linux) + +mQINBGRuSzMBEADsqiwOy78w7F4+sshaMFRIwRGNRm94p5Qey2KMZBxekFtoryVD +D9jEOnvupx4tvhfBHz5EcUHCEOdl4MTqdBy6vVAshozgxVb9RE8JpECn5lw7XC69 +4Y7Gy1TKKQMEWtDXElkGxIFdUWvWjSnPlzfnoXwQYGeE93CUS3h5dImP22Yk1Ct6 +eGGhlcbg1X4L8EpFMj7GvcsU8f7ziVI/PyC1Xwy39Q8/I67ip5eU5ddxO/xHqrbL +YC7+8pJPbRMej2twT2LrcpWWYAbprMtRoa6WfE0/thoo3xhHpIMHdPfAA86ZNGIN +kRLjGUg7jnPTRW4Oin3pCc8nT4Tfc1QERkHm641gTC/jUvpmQsM6h/FUVP2i5iE/ +JHpJcMuL2Mg6zDo3x+3gTCf+Wqz3rZzxB+wQT3yryZs6efcQy7nROiRxYBxCSXX0 +2cNYzsYLb/bYaW8yqWIHD5IqKhw269gp2E5Khs60zgS3CorMb5/xHgXjUCVgcu8a +a8ncdf9fjl3WS5p0ohetPbO2ZjWv+MaqrZOmUIgKbA4RpWZ/fU97P5BW9ylwmIDB +sWy0cMxg8MlvSdLytPieogaM0qMg3u5qXRGBr6Wmevkty0qgnmpGGc5zPiUbtOE8 +CnFFqyxBpj5IOnG0KZGVihvn+iRxrv6GO7WWO92+Dc6m94U0EEiBR7QiOwARAQAB +tDRBV1MgU0FNIENMSSBQcmltYXJ5IDxhd3Mtc2FtLWNsaS1wcmltYXJ5QGFtYXpv +bi5jb20+iQI/BBMBCQApBQJkbkszAhsvBQkHhM4ABwsJCAcDAgEGFQgCCQoLBBYC +AwECHgECF4AACgkQQv1fenOtiFqTuhAAzi5+ju5UVOWqHKevOJSO08T4QB8HcqAE +SVO3mY6/j29knkcL8ubZP/DbpV7QpHPI2PB5qSXsiDTP3IYPbeY78zHSDjljaIK3 +njJLMScFeGPyfPpwMsuY4nzrRIgAtXShPA8N/k4ZJcafnpNqKj7QnPxiC1KaIQWm +pOtvb8msUF3/s0UTa5Ys/lNRhVC0eGg32ogXGdojZA2kHZWdm9udLo4CDrDcrQT7 +NtDcJASapXSQL63XfAS3snEc4e1941YxcjfYZ33rel8K9juyDZfi1slWR/L3AviI +QFIaqSHzyOtP1oinUkoVwL8ThevKD3Ag9CZflZLzNCV7yqlF8RlhEZ4zcE/3s9El +WzCFsozb5HfE1AZonmrDh3SyOEIBMcS6vG5dWnvJrAuSYv2rX38++K5Pr/MIAfOX +DOI1rtA+XDsHNv9lSwSy0lt+iClawZANO9IXCiN1rOYcVQlwzDFwCNWDgkwdOqS0 +gOA2f8NF9lE5nBbeEuYquoOl1Vy8+ICbgOFs9LoWZlnVh7/RyY6ssowiU9vGUnHI +L8f9jqRspIz/Fm3JD86ntZxLVGkeZUz62FqErdohYfkFIVcv7GONTEyrz5HLlnpv +FJ0MR0HjrMrZrnOVZnwBKhpbLocTsH+3t5It4ReYEX0f1DIOL/KRwPvjMvBVkXY5 +hblRVDQoOWc= +=d9oG +-----END PGP PUBLIC KEY BLOCK-----" + +SIGNER_PUBLIC_KEY="-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2.0.22 (GNU/Linux) + +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 +LtZnq+n4SfeNbZjD2FQWZR4CrA== +=lHfs +-----END PGP PUBLIC KEY BLOCK-----" + +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 +} + +export DEBIAN_FRONTEND=noninteractive + +check_packages curl ca-certificates gpg dirmngr unzip bash-completion less + +verify_aws_sam_cli_gpg_signature() { + local filePath=$1 + local sigFilePath=$2 + local tmp_dir=$3 + local awsGpgKeyring="${tmp_dir}/aws-sam-cli-public-key.gpg" + + echo "${PRIMARY_PUBLIC_KEY}" | gpg --dearmor > "${awsGpgKeyring}" + echo "${SIGNER_PUBLIC_KEY}" | gpg --dearmor >> "${awsGpgKeyring}" + + gpg --batch --quiet --no-default-keyring --keyring "${awsGpgKeyring}" --verify "${sigFilePath}" "${filePath}" + local status=$? + + return ${status} +} + +install() { + tmp_dir="$(mktemp -d)" + trap 'rm -rf "${tmp_dir}"' EXIT + + local scriptZipFile="${tmp_dir}/aws-sam-cli.zip" + local scriptSigFile="${tmp_dir}/aws-sam-cli.sig" + + architecture=$(dpkg --print-architecture) + case "${architecture}" in + amd64) architectureStr=x86_64 ;; + arm64) architectureStr=arm64 ;; + *) + echo "AWS SAM CLI does not support machine architecture '$architecture'. Please use an x86-64 or ARM64 machine." + exit 1 + esac + local scriptUrl=https://github.com/aws/aws-sam-cli/releases/download/${VERSION}/aws-sam-cli-linux-${architectureStr}.zip + echo "Downloading AWS SAM CLI from ${scriptUrl}..." + curl -fsSL "${scriptUrl}" -o "${scriptZipFile}" + curl -fsSL "${scriptUrl}.sig" -o "${scriptSigFile}" + + if ! verify_aws_sam_cli_gpg_signature "$scriptZipFile" "$scriptSigFile" "$tmp_dir"; then + echo "Could not verify GPG signature of AWS SAM CLI install script. Make sure you provided a valid version." + exit 1 + fi + echo "GPG signature of AWS SAM CLI install script verified successfully. Installing..." + unzip -q "${scriptZipFile}" -d "${tmp_dir}/aws-sam-cli" + "${tmp_dir}/aws-sam-cli/install" + + echo "AWS SAM CLI installed successfully." +} + +echo "(*) Installing AWS SAM CLI..." + +install + +# Clean up +rm -rf /var/lib/apt/lists/* + +echo "Done!" diff --git a/src/base/.devcontainer/scripts/install_cfn_guard.sh b/src/base/.devcontainer/scripts/install_cfn_guard.sh new file mode 100755 index 00000000..15288543 --- /dev/null +++ b/src/base/.devcontainer/scripts/install_cfn_guard.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +VERSION=${VERSION:-"3.2.0"} +# Expected SHA256 checksums taken from https://github.com/aws-cloudformation/cloudformation-guard/releases/tag/3.2.0 +# When we change cfn-guard versions, these must be changed +sha256sum_expected_arm="sha256:aa757b599e59425fe3322dc65cfc93d2931c7f818aa1626996f8ad4852eae5ca" +sha256sum_expected_amd64="sha256:9f8c4d9f15f7dd54a37ea70a5237ba00aba682fb1e6521a744d12259961dfc13" + + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + sudo 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}/cfn-guard.tar.gz" + + if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" == "aarch64" ]; then + download_url="https://github.com/aws-cloudformation/cloudformation-guard/releases/download/${VERSION}/cfn-guard-v3-aarch64-ubuntu-latest.tar.gz" + arch_type="aarch64" + sha256sum_expected="${sha256sum_expected_arm}" + else + download_url="https://github.com/aws-cloudformation/cloudformation-guard/releases/download/${VERSION}/cfn-guard-v3-x86_64-ubuntu-latest.tar.gz" + arch_type="x86_64" + sha256sum_expected="${sha256sum_expected_amd64}" + fi + echo "Downloading cfn-guard from ${download_url}..." + 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 cfn-guard archive" + echo "Expected: ${sha256sum_expected}" + echo "Actual: sha256:${download_file_sha256sum}" + exit 1 + fi + + tar -xzf "${download_file}" -C "${tmp_dir}" + mkdir -p ~/.guard/bin + mv "${tmp_dir}/cfn-guard-v3-${arch_type}-ubuntu-latest/cfn-guard" ~/.guard/bin/cfn-guard + chmod +x ~/.guard/bin/cfn-guard +} +echo "(*) Installing cfn-guard..." + +install + +echo "Done!" diff --git a/src/base/.devcontainer/scripts/install_github_release.sh b/src/base/.devcontainer/scripts/install_github_release.sh new file mode 100755 index 00000000..673f36d2 --- /dev/null +++ b/src/base/.devcontainer/scripts/install_github_release.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +set -euo pipefail + +export DEBIAN_FRONTEND=noninteractive + +DEFAULT_INSTALL_DIR="/usr/local/bin" +INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" + +if ! command -v curl >/dev/null 2>&1 || ! command -v unzip >/dev/null 2>&1 || ! command -v tar >/dev/null 2>&1; then + apt-get update + apt-get install -y --no-install-recommends curl unzip tar ca-certificates +fi + +if ! command -v gh >/dev/null 2>&1; then + echo "GitHub CLI (gh) is required for attestation verification but was not found" + exit 1 +fi + +required_vars=( + "GITHUB_REPO" + "VERSION" + "DOWNLOAD_BINARY" + "TOOL" + "COMPRESSION" + "VERIFY_BINARY_ATTESTATION" + "VERIFY_CHECKSUM" +) + +for var_name in "${required_vars[@]}"; do + if [ -z "${!var_name:-}" ]; then + echo "${var_name} must be defined" + exit 1 + fi +done + +if [ "${VERIFY_BINARY_ATTESTATION}" != "true" ] && [ "${VERIFY_CHECKSUM}" != "true" ]; then + echo "VERIFY_BINARY_ATTESTATION or VERIFY_CHECKSUM must be set to true" + exit 1 +fi + +BINARY_URL="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${DOWNLOAD_BINARY}" +BINARY_ASSET_NAME="${DOWNLOAD_BINARY}" +CHECKSUMS_URL="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/checksums.txt" +tmp_dir="$(mktemp -d)" +trap 'rm -rf "${tmp_dir}"' EXIT + +curl -fsSL "${BINARY_URL}" -o "${tmp_dir}/${BINARY_ASSET_NAME}" +if [ "${VERIFY_BINARY_ATTESTATION}" == "true" ]; then + gh attestation verify "${tmp_dir}/${BINARY_ASSET_NAME}" -R "${GITHUB_REPO}" +fi + +if [ "${VERIFY_CHECKSUM}" == "true" ]; then + curl -fsSL "${CHECKSUMS_URL}" -o "${tmp_dir}/checksums.txt" + gh attestation verify "${tmp_dir}/checksums.txt" -R "${GITHUB_REPO}" + ( + cd "${tmp_dir}" + sha256sum --ignore-missing -c checksums.txt + ) +fi + +if [ "${COMPRESSION}" == "zip" ]; then + unzip -q "${tmp_dir}/${BINARY_ASSET_NAME}" -d "${tmp_dir}" +elif [ "${COMPRESSION}" == "tar.gz" ]; then + tar -xzf "${tmp_dir}/${BINARY_ASSET_NAME}" -C "${tmp_dir}" +else + echo "Unsupported compression format: ${COMPRESSION}" + exit 1 +fi + +mkdir -p "$INSTALL_DIR" +install -m 0755 "$tmp_dir/$TOOL" "${INSTALL_DIR}/${TOOL}" diff --git a/src/base/.devcontainer/scripts/install_gitleaks.sh b/src/base/.devcontainer/scripts/install_gitleaks.sh new file mode 100755 index 00000000..2d00a6fa --- /dev/null +++ b/src/base/.devcontainer/scripts/install_gitleaks.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +VERSION=${VERSION:-"8.30.1"} +# Expected SHA256 checksums taken from https://github.com/gitleaks/gitleaks/releases/tag/v8.30.1 +# When we change gitleaks versions, these must be changed +sha256sum_expected_arm="sha256:e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080" +sha256sum_expected_amd64="sha256:551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb" + +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}/gitleaks.tar.gz" + + if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" == "aarch64" ]; then + download_url="https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_arm64.tar.gz" + sha256sum_expected="${sha256sum_expected_arm}" + else + download_url="https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" + sha256sum_expected="${sha256sum_expected_amd64}" + fi + echo "Downloading gitleaks from ${download_url}..." + 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 gitleaks 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}/gitleaks" /usr/bin/gitleaks + chmod +x /usr/bin/gitleaks +} +echo "(*) Installing gitleaks..." + +install + +echo "Done!" diff --git a/src/base/.devcontainer/scripts/root_install.sh b/src/base/.devcontainer/scripts/root_install.sh index 386f3981..43eeddd0 100755 --- a/src/base/.devcontainer/scripts/root_install.sh +++ b/src/base/.devcontainer/scripts/root_install.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -set -e +set -euo pipefail + export DEBIAN_FRONTEND=noninteractive # Add amd64 architecture if on arm64 @@ -31,33 +32,15 @@ apt-get -y install --no-install-recommends htop vim curl git build-essential \ libreadline-dev wget llvm libncurses5-dev libncursesw5-dev \ xz-utils tk-dev liblzma-dev netcat-traditional libyaml-dev uuid-runtime xxd unzip -# Download correct SAM CLI for arch -echo "Installing aws-sam cli" -if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" = "aarch64" ]; then - wget -O /tmp/aws-sam-cli.zip --no-verbose "https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-arm64.zip" - else - wget -O /tmp/aws-sam-cli.zip --no-verbose "https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip" - fi - unzip -q /tmp/aws-sam-cli.zip -d /tmp/aws-sam-cli - /tmp/aws-sam-cli/install - rm /tmp/aws-sam-cli.zip - rm -rf /tmp/aws-sam-cli - +# install AWS SAM CLI +VERSION="${SAM_VERSION}" "${SCRIPTS_DIR}/${CONTAINER_NAME}/install_aws_sam_cli.sh" # Install ASDF -echo "Installing asdf" -ASDF_VERSION=$(awk '!/^#/ && NF {print $1; exit}' "${SCRIPTS_DIR}/${CONTAINER_NAME}/.tool-versions.asdf") -if [ "$TARGETARCH" = "arm64" ] || [ "$TARGETARCH" == "aarch64" ]; then - wget -O /tmp/asdf.tar.gz --no-verbose "https://github.com/asdf-vm/asdf/releases/download/v${ASDF_VERSION}/asdf-v${ASDF_VERSION}-linux-arm64.tar.gz" -else - wget -O /tmp/asdf.tar.gz --no-verbose "https://github.com/asdf-vm/asdf/releases/download/v${ASDF_VERSION}/asdf-v${ASDF_VERSION}-linux-amd64.tar.gz" -fi -tar -xzf /tmp/asdf.tar.gz -C /tmp -mkdir -p /usr/bin -mv /tmp/asdf /usr/bin/asdf -chmod +x /usr/bin/asdf -rm -rf /tmp/asdf.tar.gz +VERSION="${ASDF_VERSION}" "${SCRIPTS_DIR}/${CONTAINER_NAME}/install_asdf.sh" +# install gitleaks +VERSION="${GITLEAKS_VERSION}" "${SCRIPTS_DIR}/${CONTAINER_NAME}/install_gitleaks.sh" # install gitsecrets +# this should be removed once we have migrated all repos to gitleaks git clone https://github.com/awslabs/git-secrets.git /tmp/git-secrets cd /tmp/git-secrets make install @@ -68,10 +51,13 @@ chmod 755 /usr/share/secrets-scanner curl -L https://raw.githubusercontent.com/NHSDigital/software-engineering-quality-framework/main/tools/nhsd-git-secrets/nhsd-rules-deny.txt -o /usr/share/secrets-scanner/nhsd-rules-deny.txt # get cfn-guard ruleset -wget -O /tmp/ruleset.zip https://github.com/aws-cloudformation/aws-guard-rules-registry/releases/download/1.0.2/ruleset-build-v1.0.2.zip >/dev/null 2>&1 +tmp_dir="$(mktemp -d)" +trap 'rm -rf "${tmp_dir}"' EXIT +download_file="${tmp_dir}/ruleset.zip" +curl -fsSL "https://github.com/aws-cloudformation/aws-guard-rules-registry/releases/download/1.0.2/ruleset-build-v1.0.2.zip" -o "${download_file}" + mkdir -p "${SCRIPTS_DIR}/cfnguard_rulesets" -unzip /tmp/ruleset.zip -d "${SCRIPTS_DIR}/cfnguard_rulesets" >/dev/null 2>&1 -rm /tmp/ruleset.zip +unzip "${download_file}" -d "${SCRIPTS_DIR}/cfnguard_rulesets" # clean up apt-get clean diff --git a/src/base/.devcontainer/scripts/vscode_install.sh b/src/base/.devcontainer/scripts/vscode_install.sh index e14f35ac..49c07c7f 100755 --- a/src/base/.devcontainer/scripts/vscode_install.sh +++ b/src/base/.devcontainer/scripts/vscode_install.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash - -set -e +set -euo pipefail # shellcheck disable=SC2129 # shellcheck disable=SC2016 @@ -21,7 +20,7 @@ asdf plugin add terraform https://github.com/asdf-community/asdf-hashicorp.git asdf plugin add yq https://github.com/sudermanjr/asdf-yq.git # install cfn-guard -curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.sh | sh +VERSION="${CFN_GUARD_VERSION}" "${SCRIPTS_DIR}/${CONTAINER_NAME}/install_cfn_guard.sh" # install base asdf versions of common tools cd /home/vscode diff --git a/src/languages/node_24_python_3_10/.devcontainer/scripts/requirements-user.txt b/src/languages/node_24_python_3_10/.devcontainer/scripts/requirements-user.txt deleted file mode 100644 index 37e7b2fe..00000000 --- a/src/languages/node_24_python_3_10/.devcontainer/scripts/requirements-user.txt +++ /dev/null @@ -1 +0,0 @@ -zizmor==1.23.1 diff --git a/src/languages/node_24_python_3_10/.devcontainer/scripts/vscode_install.sh b/src/languages/node_24_python_3_10/.devcontainer/scripts/vscode_install.sh index a82f26f5..f13cedf2 100755 --- a/src/languages/node_24_python_3_10/.devcontainer/scripts/vscode_install.sh +++ b/src/languages/node_24_python_3_10/.devcontainer/scripts/vscode_install.sh @@ -1,11 +1,8 @@ #!/usr/bin/env bash set -e -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) asdf plugin add python asdf plugin add poetry https://github.com/asdf-community/asdf-poetry.git asdf install python asdf install - -pip install --user -r "${SCRIPT_DIR}/requirements-user.txt" diff --git a/src/languages/node_24_python_3_12/.devcontainer/scripts/requirements-user.txt b/src/languages/node_24_python_3_12/.devcontainer/scripts/requirements-user.txt index ac968564..58587fdf 100644 --- a/src/languages/node_24_python_3_12/.devcontainer/scripts/requirements-user.txt +++ b/src/languages/node_24_python_3_12/.devcontainer/scripts/requirements-user.txt @@ -1,2 +1 @@ -zizmor==1.23.1 cfn-lint==1.47.1 diff --git a/src/languages/node_24_python_3_12/.devcontainer/scripts/vscode_install.sh b/src/languages/node_24_python_3_12/.devcontainer/scripts/vscode_install.sh index a82f26f5..14bd8c1d 100755 --- a/src/languages/node_24_python_3_12/.devcontainer/scripts/vscode_install.sh +++ b/src/languages/node_24_python_3_12/.devcontainer/scripts/vscode_install.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash set -e -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) asdf plugin add python asdf plugin add poetry https://github.com/asdf-community/asdf-poetry.git diff --git a/src/languages/node_24_python_3_13/.devcontainer/scripts/requirements-user.txt b/src/languages/node_24_python_3_13/.devcontainer/scripts/requirements-user.txt index ac968564..58587fdf 100644 --- a/src/languages/node_24_python_3_13/.devcontainer/scripts/requirements-user.txt +++ b/src/languages/node_24_python_3_13/.devcontainer/scripts/requirements-user.txt @@ -1,2 +1 @@ -zizmor==1.23.1 cfn-lint==1.47.1 diff --git a/src/languages/node_24_python_3_13/.devcontainer/scripts/vscode_install.sh b/src/languages/node_24_python_3_13/.devcontainer/scripts/vscode_install.sh index a82f26f5..14bd8c1d 100755 --- a/src/languages/node_24_python_3_13/.devcontainer/scripts/vscode_install.sh +++ b/src/languages/node_24_python_3_13/.devcontainer/scripts/vscode_install.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash set -e -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) asdf plugin add python asdf plugin add poetry https://github.com/asdf-community/asdf-poetry.git diff --git a/src/languages/node_24_python_3_14/.devcontainer/scripts/requirements-user.txt b/src/languages/node_24_python_3_14/.devcontainer/scripts/requirements-user.txt index ac968564..58587fdf 100644 --- a/src/languages/node_24_python_3_14/.devcontainer/scripts/requirements-user.txt +++ b/src/languages/node_24_python_3_14/.devcontainer/scripts/requirements-user.txt @@ -1,2 +1 @@ -zizmor==1.23.1 cfn-lint==1.47.1 diff --git a/src/languages/node_24_python_3_14/.devcontainer/scripts/vscode_install.sh b/src/languages/node_24_python_3_14/.devcontainer/scripts/vscode_install.sh index a82f26f5..14bd8c1d 100755 --- a/src/languages/node_24_python_3_14/.devcontainer/scripts/vscode_install.sh +++ b/src/languages/node_24_python_3_14/.devcontainer/scripts/vscode_install.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash set -e -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) asdf plugin add python asdf plugin add poetry https://github.com/asdf-community/asdf-poetry.git diff --git a/src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint b/src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint deleted file mode 100644 index d6a1e223..00000000 --- a/src/projects/eps-storage-terraform/.devcontainer/Dockerfile.tflint +++ /dev/null @@ -1,13 +0,0 @@ -FROM serversideup/github-cli:2.89.0 AS build -ARG TARGETARCH -ARG TFLINT_VERSION="v0.61.0" -COPY --chmod=755 scripts/install_tflint.sh /tmp/install_tflint.sh -RUN --mount=type=secret,id=GH_TOKEN,env=GH_TOKEN \ - INSTALL_DIR=/tmp/tflint/ \ - ARCH="${TARGETARCH}" \ - VERSION="${TFLINT_VERSION}" \ - /tmp/install_tflint.sh - -FROM scratch -COPY --from=build /tmp/tflint/tflint /tflint -ENTRYPOINT ["/tflint"] diff --git a/src/projects/eps-storage-terraform/.devcontainer/scripts/install_tflint.sh b/src/projects/eps-storage-terraform/.devcontainer/scripts/install_tflint.sh deleted file mode 100755 index 1f62116e..00000000 --- a/src/projects/eps-storage-terraform/.devcontainer/scripts/install_tflint.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail -export DEBIAN_FRONTEND=noninteractive - -DEFAULT_INSTALL_DIR="/usr/local/bin" -INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" - -case "${TARGETARCH:-}" in - amd64|arm64) - TFLINT_ARCH="${TARGETARCH}" - ;; - *) - echo "Unsupported or missing TARGETARCH: '${TARGETARCH:-}'" - echo "Expected one of: amd64, arm64" - exit 1 - ;; -esac - -if ! command -v curl >/dev/null 2>&1 || ! command -v unzip >/dev/null 2>&1; then - apt-get update - apt-get install -y --no-install-recommends curl unzip ca-certificates -fi - -if ! command -v gh >/dev/null 2>&1; then - echo "GitHub CLI (gh) is required for attestation verification but was not found" - exit 1 -fi - -TFLINT_URL="https://github.com/terraform-linters/tflint/releases/download/${TFLINT_VERSION}/tflint_linux_${TFLINT_ARCH}.zip" -TFLINT_ASSET_NAME="tflint_linux_${TFLINT_ARCH}.zip" -CHECKSUMS_URL="https://github.com/terraform-linters/tflint/releases/download/${TFLINT_VERSION}/checksums.txt" -tmp_dir="$(mktemp -d)" -trap 'rm -rf "${tmp_dir}"' EXIT - -curl -fsSL "${CHECKSUMS_URL}" -o "${tmp_dir}/checksums.txt" -gh attestation verify "${tmp_dir}/checksums.txt" -R terraform-linters/tflint - -curl -fsSL "${TFLINT_URL}" -o "${tmp_dir}/${TFLINT_ASSET_NAME}" -( - cd "${tmp_dir}" - sha256sum --ignore-missing -c checksums.txt -) - -unzip -q "${tmp_dir}/${TFLINT_ASSET_NAME}" -d "${tmp_dir}" - -mkdir -p "$INSTALL_DIR" -install -m 0755 "$tmp_dir/tflint" "${INSTALL_DIR}/tflint"