From 8cb237e809b2aff1d623574c9101f0eb16defdc2 Mon Sep 17 00:00:00 2001 From: Gaurav Tiwari Date: Thu, 23 Jul 2026 11:35:01 +0100 Subject: [PATCH] Prepare CLI distribution for 1.13.100 --- .github/SECURITY.md | 29 +++++++++ .github/dependabot.yml | 11 ++++ .github/workflows/verify.yml | 42 ++++++++++++ README.md | 2 +- docs/github-actions.md | 12 ++-- docs/quick-start.md | 2 +- docs/tool-guides.md | 2 +- install-web/index.html | 2 +- install-web/install.sh | 88 ++++++++++++++++--------- install.sh | 88 ++++++++++++++++--------- scripts/test-installer-trust.sh | 110 ++++++++++++++++++++++++++++++++ 11 files changed, 320 insertions(+), 68 deletions(-) create mode 100644 .github/SECURITY.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/verify.yml create mode 100755 scripts/test-installer-trust.sh diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..23076dec --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Reporting a Vulnerability + +Please do not open a public issue or pull request for a potential security +vulnerability. Use GitHub's private vulnerability reporting for the public CLI +distribution repository: + +https://github.com/boringcache/cli/security/advisories/new + +Include the CLI version, platform, relevant release or workflow link, expected +impact, and enough reproduction detail for us to verify the report. Do not +include live credentials, customer data, or secrets. + +## Supported Versions + +The latest published CLI release is supported. Security fixes are normally +shipped as a new release rather than backported to older binaries. + +## Scope + +This public repository is the CLI distribution channel. It owns the installer, +release binaries, checksums, signed checksum bundles, release notes, and public +documentation. Product source is maintained separately in the private +BoringCache monorepo. + +Reports about the CLI, installer, release pipeline, artifact integrity, or the +authorization boundary between the CLI and BoringCache service are all welcome +through the private reporting link above. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..2664eb0d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 + +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 00000000..6f5eb84d --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,42 @@ +name: Verify Distribution + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: verify-distribution-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + verify: + name: Verify distribution + runs-on: ubuntu-24.04 + timeout-minutes: 5 + + steps: + - name: Checkout distribution + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Check installer syntax + run: | + sh -n install.sh + sh -n install-web/install.sh + sh -n scripts/test-installer-trust.sh + + - name: Test installer trust contract + run: scripts/test-installer-trust.sh + + - name: Check public trust files + run: | + test -s .github/SECURITY.md + if grep -R -n --exclude-dir=.git 'boringcache/one@v1' README.md INSTALLATION.md docs install-web; then + echo 'Mutable boringcache/one references are not allowed in public guidance.' >&2 + exit 1 + fi diff --git a/README.md b/README.md index 7f6a7808..05afd86b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Use adapter commands when the build tool already speaks a remote-cache protocol Use `cache-registry` when the repo already has a checked-in local endpoint setup or another process should keep the proxy alive. `cache-registry` is the proxy. `run --proxy` and adapter commands temporarily start that same proxy for one command. When `.boringcache.toml` stores the Docker command, `boringcache docker` is the short form. Use the longer version when you want to pass the Docker command inline. -If you are wiring GitHub Actions, use [`boringcache/one@v1`](https://github.com/boringcache/one) after onboard so CI can reuse the same repo config and trust model. +If you are wiring GitHub Actions, use [`boringcache/one`](https://github.com/boringcache/one) pinned to the immutable v1.13.99 commit (`b55458ec8a4165e3fd70b1a1645f518a2095ed02`) after onboard so CI can reuse the same repo config and trust model. ## Docs diff --git a/docs/github-actions.md b/docs/github-actions.md index 3fff4281..839b4ec2 100644 --- a/docs/github-actions.md +++ b/docs/github-actions.md @@ -5,14 +5,14 @@ The preferred path is: 1. install the CLI locally 2. run `boringcache onboard` 3. commit `.boringcache.toml` when it helps -4. use [`boringcache/one@v1`](https://github.com/boringcache/one) in GitHub Actions +4. use [`boringcache/one`](https://github.com/boringcache/one) pinned to the immutable v1.13.99 commit in GitHub Actions That keeps CI and local runs on the same workspace, entries, and cache profiles. Example: ```yaml -- uses: boringcache/one@v1 +- uses: boringcache/one@b55458ec8a4165e3fd70b1a1645f518a2095ed02 # v1.13.99 with: workspace: my-org/my-project cache-profiles: bundle-install @@ -21,10 +21,10 @@ Example: BORINGCACHE_SAVE_TOKEN: ${{ secrets.BORINGCACHE_SAVE_TOKEN }} ``` -For proxy-backed modes, `boringcache/one@v1` also accepts first-class `metadata-hints` so sessions and misses stay grouped by stable labels instead of per-run noise: +For proxy-backed modes, the pinned `boringcache/one` action also accepts first-class `metadata-hints` so sessions and misses stay grouped by stable labels instead of per-run noise: ```yaml -- uses: boringcache/one@v1 +- uses: boringcache/one@b55458ec8a4165e3fd70b1a1645f518a2095ed02 # v1.13.99 with: mode: bazel workspace: my-org/my-project @@ -39,7 +39,7 @@ For proxy-backed modes, `boringcache/one@v1` also accepts first-class `metadata- Keep those hints low-cardinality. Good values are `project=web`, `benchmark=grpc-bazel`, `tool=gradle`, `lane=ci`, or `workflow=build`. Avoid commit SHAs, run ids, timestamps, and cold/warm labels for normal sessions; BoringCache classifies new and recurring misses from cache target and lifecycle data. -If the repo already defines `[proxy]` or adapter `metadata-hints` in `.boringcache.toml`, `boringcache/one@v1` inherits them through the CLI dry-run plan. Prefer repo config for durable defaults and use the action input only when the workflow needs an explicit override. +If the repo already defines `[proxy]` or adapter `metadata-hints` in `.boringcache.toml`, the pinned `boringcache/one` action inherits them through the CLI dry-run plan. Prefer repo config for durable defaults and use the action input only when the workflow needs an explicit override. The canonical repo-config starting points in [Tool guides](tool-guides.md) are meant to be shared between local CLI runs and GitHub Actions for exactly this reason. @@ -81,7 +81,7 @@ You can still override a configured adapter from the workflow when needed: BORINGCACHE_SAVE_TOKEN: ${{ secrets.BORINGCACHE_SAVE_TOKEN }} ``` -Use `boringcache/one@v1` when you want the action to keep owning tool setup such as Bazel rc files, Maven or Gradle cache config, buildx setup, or container networking. +Use the pinned `boringcache/one` action when you want it to keep owning tool setup such as Bazel rc files, Maven or Gradle cache config, buildx setup, or container networking. When you run `boringcache docker` directly in GitHub Actions, the CLI derives the same branch/default/PR human tags from GitHub metadata that archive and proxy flows use. The action path passes provider-neutral metadata so Docker cache artifacts report the resolved human import/export tags and CI context. For Docker and BuildKit registry caches on pull requests, restore-only is the default. diff --git a/docs/quick-start.md b/docs/quick-start.md index b9b655c9..ec47385c 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -52,5 +52,5 @@ boringcache docker The next docs to read are usually [Adapter commands](adapter-commands.md) and [Tool guides](tool-guides.md). -If the repo uses GitHub Actions, the next step is usually [`boringcache/one@v1`](https://github.com/boringcache/one). +If the repo uses GitHub Actions, the next step is usually [`boringcache/one`](https://github.com/boringcache/one) pinned to the immutable v1.13.99 commit (`b55458ec8a4165e3fd70b1a1645f518a2095ed02`). See [GitHub Actions](github-actions.md). diff --git a/docs/tool-guides.md b/docs/tool-guides.md index 41b16319..dafe4516 100644 --- a/docs/tool-guides.md +++ b/docs/tool-guides.md @@ -14,7 +14,7 @@ The pattern is simple: `cache-registry` itself is warm by default. Use `--on-demand` only for advanced shared-proxy setups that prefer immediate startup over warmed first reads. The snippets below are intended to be copy-pasteable `.boringcache.toml` -starting points. They work for local CLI runs and for `boringcache/one@v1` +starting points. They work for local CLI runs and for the pinned `boringcache/one` action because the action asks the CLI for the same repo plan. Shared defaults for the examples: diff --git a/install-web/index.html b/install-web/index.html index 4a341e5f..8422cd6c 100644 --- a/install-web/index.html +++ b/install-web/index.html @@ -188,7 +188,7 @@

🎯 Next Steps

  • Move into your repo: cd your-project
  • Connect the CLI: boringcache onboard
  • Wrap one repeated step: boringcache run -- bundle install
  • -
  • Wire CI when ready: boringcache/one@v1
  • +
  • Wire CI when ready: boringcache/one@b55458ec8a4165e3fd70b1a1645f518a2095ed02 (v1.13.99)
  • 📖 Documentation: Visit boringcache.com/docs for setup paths and CLI docs.

    diff --git a/install-web/install.sh b/install-web/install.sh index 3247cf1b..88aa146a 100644 --- a/install-web/install.sh +++ b/install-web/install.sh @@ -1,6 +1,7 @@ #!/bin/sh # BoringCache CLI Installation Script # Usage: curl -sSL -H "Cache-Control: no-cache" -H "Pragma: no-cache" https://install.boringcache.com/install.sh | sh +# Strict: curl -sSL https://install.boringcache.com/install.sh | BORINGCACHE_VERIFY_SIGNATURE=1 sh set -e @@ -14,6 +15,9 @@ NC='\033[0m' # No Color # GitHub repository REPO="boringcache/cli" BINARY_NAME="boringcache" +CHECKSUM_CERTIFICATE_IDENTITY_REGEXP='^https://github\.com/boringcache/monorepo/\.github/workflows/(cli-release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+|cli-release-checksums\.yml@refs/heads/main)$' +CHECKSUM_CERTIFICATE_OIDC_ISSUER='https://token.actions.githubusercontent.com' +VERIFY_CHECKSUM_SIGNATURE=0 # Function to print colored output print_status() { @@ -69,7 +73,7 @@ get_latest_release() { # If API fails (e.g., private repo), fall back to known version # This should be updated when new versions are released - local fallback_version="v1.13.99" + local fallback_version="v1.13.100" print_warning "GitHub API unavailable, using fallback version: $fallback_version" >&2 print_warning "This may not be the latest version. Check https://github.com/${repo}/releases manually." >&2 @@ -110,33 +114,52 @@ verify_checksum() { fi } +prepare_checksum_signature_verification() { + VERIFY_CHECKSUM_SIGNATURE=0 + + case "${BORINGCACHE_VERIFY_SIGNATURE:-auto}" in + auto) + if command -v cosign >/dev/null 2>&1; then + VERIFY_CHECKSUM_SIGNATURE=1 + print_status "cosign found; release signature verification is enabled." + else + print_warning "cosign not found; continuing with SHA-256 checksum verification." + print_warning "Install cosign and set BORINGCACHE_VERIFY_SIGNATURE=1 for fail-closed signature verification." + fi + ;; + 1|true|required) + if ! command -v cosign >/dev/null 2>&1; then + print_error "BORINGCACHE_VERIFY_SIGNATURE=1 requires cosign in PATH." + return 1 + fi + VERIFY_CHECKSUM_SIGNATURE=1 + ;; + 0|false|off) + ;; + *) + print_error "BORINGCACHE_VERIFY_SIGNATURE must be auto, 1, or 0." + return 1 + ;; + esac +} + verify_checksum_signature() { local temp_dir="$1" - if [ "${BORINGCACHE_VERIFY_SIGNATURE:-0}" != "1" ]; then + if [ "${VERIFY_CHECKSUM_SIGNATURE}" != "1" ]; then return 0 fi - if ! command -v cosign >/dev/null 2>&1; then - print_error "BORINGCACHE_VERIFY_SIGNATURE=1 requires cosign in PATH." - print_error "Install cosign or unset BORINGCACHE_VERIFY_SIGNATURE to use checksum-only verification." - exit 1 + if [ ! -s "${temp_dir}/SHA256SUMS.bundle" ]; then + print_error "Signed checksum bundle is missing or empty." + return 1 fi - if [ -f "${temp_dir}/SHA256SUMS.bundle" ]; then - cosign verify-blob \ - --bundle "${temp_dir}/SHA256SUMS.bundle" \ - --certificate-identity-regexp '^https://github.com/boringcache/.+/.github/workflows/.+@refs/(heads/main|tags/v.+)$' \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - "${temp_dir}/SHA256SUMS" >/dev/null - else - cosign verify-blob \ - --certificate "${temp_dir}/SHA256SUMS.pem" \ - --signature "${temp_dir}/SHA256SUMS.sig" \ - --certificate-identity-regexp '^https://github.com/boringcache/.+/.github/workflows/.+@refs/(heads/main|tags/v.+)$' \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - "${temp_dir}/SHA256SUMS" >/dev/null - fi + cosign verify-blob \ + --bundle "${temp_dir}/SHA256SUMS.bundle" \ + --certificate-identity-regexp "${CHECKSUM_CERTIFICATE_IDENTITY_REGEXP}" \ + --certificate-oidc-issuer "${CHECKSUM_CERTIFICATE_OIDC_ISSUER}" \ + "${temp_dir}/SHA256SUMS" >/dev/null } # Function to download and install binary @@ -187,14 +210,20 @@ install_binary() { # Create temporary directory local temp_dir=$(mktemp -d) local temp_file="${temp_dir}/${binary_name}" + + if ! prepare_checksum_signature_verification; then + rm -rf "${temp_dir}" + exit 1 + fi download_file "${download_url}" "${temp_file}" download_file "${release_url}/SHA256SUMS" "${temp_dir}/SHA256SUMS" - if [ "${BORINGCACHE_VERIFY_SIGNATURE:-0}" = "1" ]; then + if [ "${VERIFY_CHECKSUM_SIGNATURE}" = "1" ]; then if ! download_file "${release_url}/SHA256SUMS.bundle" "${temp_dir}/SHA256SUMS.bundle"; then rm -f "${temp_dir}/SHA256SUMS.bundle" - download_file "${release_url}/SHA256SUMS.sig" "${temp_dir}/SHA256SUMS.sig" - download_file "${release_url}/SHA256SUMS.pem" "${temp_dir}/SHA256SUMS.pem" + print_error "Signed checksum bundle is unavailable for ${version}." + rm -rf "${temp_dir}" + exit 1 fi fi @@ -205,13 +234,13 @@ install_binary() { exit 1 fi - if ! verify_checksum "${temp_dir}" "${binary_name}"; then - print_error "Checksum verification failed for ${binary_name}" + if ! verify_checksum_signature "${temp_dir}"; then + print_error "Checksum signature verification failed" exit 1 fi - if ! verify_checksum_signature "${temp_dir}"; then - print_error "Checksum signature verification failed" + if ! verify_checksum "${temp_dir}" "${binary_name}"; then + print_error "Checksum verification failed for ${binary_name}" exit 1 fi @@ -329,5 +358,6 @@ main() { print_status "📖 Docs: https://boringcache.com/docs" } -# Run main function -main "$@" +if [ "${BORINGCACHE_INSTALLER_SOURCE_ONLY:-0}" != "1" ]; then + main "$@" +fi diff --git a/install.sh b/install.sh index 65b5ac05..1413602a 100755 --- a/install.sh +++ b/install.sh @@ -1,6 +1,7 @@ #!/bin/sh # BoringCache CLI Installation Script # Usage: curl -sSL -H "Cache-Control: no-cache" -H "Pragma: no-cache" https://install.boringcache.com/install.sh | sh +# Strict: curl -sSL https://install.boringcache.com/install.sh | BORINGCACHE_VERIFY_SIGNATURE=1 sh set -e @@ -14,6 +15,9 @@ NC='\033[0m' # No Color # GitHub repository REPO="boringcache/cli" BINARY_NAME="boringcache" +CHECKSUM_CERTIFICATE_IDENTITY_REGEXP='^https://github\.com/boringcache/monorepo/\.github/workflows/(cli-release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+|cli-release-checksums\.yml@refs/heads/main)$' +CHECKSUM_CERTIFICATE_OIDC_ISSUER='https://token.actions.githubusercontent.com' +VERIFY_CHECKSUM_SIGNATURE=0 # Function to print colored output print_status() { @@ -69,7 +73,7 @@ get_latest_release() { # If API fails (e.g., private repo), fall back to known version # This should be updated when new versions are released - local fallback_version="v1.13.99" + local fallback_version="v1.13.100" print_warning "GitHub API unavailable, using fallback version: $fallback_version" >&2 print_warning "This may not be the latest version. Check https://github.com/${repo}/releases manually." >&2 @@ -111,33 +115,52 @@ verify_checksum() { fi } +prepare_checksum_signature_verification() { + VERIFY_CHECKSUM_SIGNATURE=0 + + case "${BORINGCACHE_VERIFY_SIGNATURE:-auto}" in + auto) + if command -v cosign >/dev/null 2>&1; then + VERIFY_CHECKSUM_SIGNATURE=1 + print_status "cosign found; release signature verification is enabled." + else + print_warning "cosign not found; continuing with SHA-256 checksum verification." + print_warning "Install cosign and set BORINGCACHE_VERIFY_SIGNATURE=1 for fail-closed signature verification." + fi + ;; + 1|true|required) + if ! command -v cosign >/dev/null 2>&1; then + print_error "BORINGCACHE_VERIFY_SIGNATURE=1 requires cosign in PATH." + return 1 + fi + VERIFY_CHECKSUM_SIGNATURE=1 + ;; + 0|false|off) + ;; + *) + print_error "BORINGCACHE_VERIFY_SIGNATURE must be auto, 1, or 0." + return 1 + ;; + esac +} + verify_checksum_signature() { local temp_dir="$1" - if [ "${BORINGCACHE_VERIFY_SIGNATURE:-0}" != "1" ]; then + if [ "${VERIFY_CHECKSUM_SIGNATURE}" != "1" ]; then return 0 fi - if ! command -v cosign >/dev/null 2>&1; then - print_error "BORINGCACHE_VERIFY_SIGNATURE=1 requires cosign in PATH." - print_error "Install cosign or unset BORINGCACHE_VERIFY_SIGNATURE to use checksum-only verification." - exit 1 + if [ ! -s "${temp_dir}/SHA256SUMS.bundle" ]; then + print_error "Signed checksum bundle is missing or empty." + return 1 fi - if [ -f "${temp_dir}/SHA256SUMS.bundle" ]; then - cosign verify-blob \ - --bundle "${temp_dir}/SHA256SUMS.bundle" \ - --certificate-identity-regexp '^https://github.com/boringcache/.+/.github/workflows/.+@refs/(heads/main|tags/v.+)$' \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - "${temp_dir}/SHA256SUMS" >/dev/null - else - cosign verify-blob \ - --certificate "${temp_dir}/SHA256SUMS.pem" \ - --signature "${temp_dir}/SHA256SUMS.sig" \ - --certificate-identity-regexp '^https://github.com/boringcache/.+/.github/workflows/.+@refs/(heads/main|tags/v.+)$' \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - "${temp_dir}/SHA256SUMS" >/dev/null - fi + cosign verify-blob \ + --bundle "${temp_dir}/SHA256SUMS.bundle" \ + --certificate-identity-regexp "${CHECKSUM_CERTIFICATE_IDENTITY_REGEXP}" \ + --certificate-oidc-issuer "${CHECKSUM_CERTIFICATE_OIDC_ISSUER}" \ + "${temp_dir}/SHA256SUMS" >/dev/null } # Function to download and install binary @@ -188,14 +211,20 @@ install_binary() { # Create temporary directory local temp_dir=$(mktemp -d) local temp_file="${temp_dir}/${binary_name}" + + if ! prepare_checksum_signature_verification; then + rm -rf "${temp_dir}" + exit 1 + fi download_file "${download_url}" "${temp_file}" download_file "${release_url}/SHA256SUMS" "${temp_dir}/SHA256SUMS" - if [ "${BORINGCACHE_VERIFY_SIGNATURE:-0}" = "1" ]; then + if [ "${VERIFY_CHECKSUM_SIGNATURE}" = "1" ]; then if ! download_file "${release_url}/SHA256SUMS.bundle" "${temp_dir}/SHA256SUMS.bundle"; then rm -f "${temp_dir}/SHA256SUMS.bundle" - download_file "${release_url}/SHA256SUMS.sig" "${temp_dir}/SHA256SUMS.sig" - download_file "${release_url}/SHA256SUMS.pem" "${temp_dir}/SHA256SUMS.pem" + print_error "Signed checksum bundle is unavailable for ${version}." + rm -rf "${temp_dir}" + exit 1 fi fi @@ -206,13 +235,13 @@ install_binary() { exit 1 fi - if ! verify_checksum "${temp_dir}" "${binary_name}"; then - print_error "Checksum verification failed for ${binary_name}" + if ! verify_checksum_signature "${temp_dir}"; then + print_error "Checksum signature verification failed" exit 1 fi - if ! verify_checksum_signature "${temp_dir}"; then - print_error "Checksum signature verification failed" + if ! verify_checksum "${temp_dir}" "${binary_name}"; then + print_error "Checksum verification failed for ${binary_name}" exit 1 fi @@ -323,5 +352,6 @@ main() { print_status " ${BINARY_NAME} onboard" } -# Run main function -main "$@" +if [ "${BORINGCACHE_INSTALLER_SOURCE_ONLY:-0}" != "1" ]; then + main "$@" +fi diff --git a/scripts/test-installer-trust.sh b/scripts/test-installer-trust.sh new file mode 100755 index 00000000..26ee8fa2 --- /dev/null +++ b/scripts/test-installer-trust.sh @@ -0,0 +1,110 @@ +#!/bin/sh + +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +CLI_ROOT=$(CDPATH= cd -- "${SCRIPT_DIR}/.." && pwd) +TEST_ROOT=$(mktemp -d) +ORIGINAL_PATH=${PATH} +EXPECTED_IDENTITY_REGEXP='^https://github\.com/boringcache/monorepo/\.github/workflows/(cli-release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+|cli-release-checksums\.yml@refs/heads/main)$' + +cleanup() { + rm -rf "${TEST_ROOT}" +} + +fail() { + printf 'installer trust test failed: %s\n' "$1" >&2 + exit 1 +} + +write_checksum() { + directory="$1" + binary_name="$2" + + if command -v sha256sum >/dev/null 2>&1; then + (cd "${directory}" && sha256sum "${binary_name}" > SHA256SUMS) + else + checksum=$(shasum -a 256 "${directory}/${binary_name}" | awk '{print $1}') + printf '%s %s\n' "${checksum}" "${binary_name}" > "${directory}/SHA256SUMS" + fi +} + +test_installer() { + installer="$1" + fixture_name="$2" + fixture_dir="${TEST_ROOT}/${fixture_name}" + fake_bin="${fixture_dir}/bin" + cosign_args="${fixture_dir}/cosign-args" + binary_name="boringcache-linux-amd64" + + mkdir -p "${fake_bin}" + BORINGCACHE_INSTALLER_SOURCE_ONLY=1 + export BORINGCACHE_INSTALLER_SOURCE_ONLY + # shellcheck source=/dev/null + . "${installer}" + + [ "${CHECKSUM_CERTIFICATE_IDENTITY_REGEXP}" = "${EXPECTED_IDENTITY_REGEXP}" ] || + fail "${fixture_name} trusts an unexpected workflow identity" + + printf 'release binary\n' > "${fixture_dir}/${binary_name}" + write_checksum "${fixture_dir}" "${binary_name}" + verify_checksum "${fixture_dir}" "${binary_name}" || + fail "${fixture_name} rejected a matching checksum" + + printf 'tampered release binary\n' > "${fixture_dir}/${binary_name}" + if verify_checksum "${fixture_dir}" "${binary_name}" >/dev/null 2>&1; then + fail "${fixture_name} accepted a checksum mismatch" + fi + + printf '404: Not Found\n' > "${fixture_dir}/${binary_name}" + if verify_checksum "${fixture_dir}" "${binary_name}" >/dev/null 2>&1; then + fail "${fixture_name} accepted an HTTP error body as a release binary" + fi + + VERIFY_CHECKSUM_SIGNATURE=1 + rm -f "${fixture_dir}/SHA256SUMS.bundle" + if verify_checksum_signature "${fixture_dir}" >/dev/null 2>&1; then + fail "${fixture_name} accepted a missing signature bundle" + fi + + if ( + PATH=/usr/bin:/bin + BORINGCACHE_VERIFY_SIGNATURE=1 + VERIFY_CHECKSUM_SIGNATURE=0 + prepare_checksum_signature_verification >/dev/null 2>&1 + ); then + fail "${fixture_name} allowed strict verification without cosign" + fi + + printf '%s\n' '#!/bin/sh' 'printf "%s\n" "$@" > "${COSIGN_ARGS_FILE}"' > "${fake_bin}/cosign" + chmod +x "${fake_bin}/cosign" + PATH="${fake_bin}:${ORIGINAL_PATH}" + COSIGN_ARGS_FILE="${cosign_args}" + BORINGCACHE_VERIFY_SIGNATURE=1 + VERIFY_CHECKSUM_SIGNATURE=0 + export PATH COSIGN_ARGS_FILE BORINGCACHE_VERIFY_SIGNATURE VERIFY_CHECKSUM_SIGNATURE + + prepare_checksum_signature_verification || + fail "${fixture_name} did not enable strict verification with cosign present" + [ "${VERIFY_CHECKSUM_SIGNATURE}" = "1" ] || + fail "${fixture_name} did not record strict verification state" + + printf 'signed bundle fixture\n' > "${fixture_dir}/SHA256SUMS.bundle" + verify_checksum_signature "${fixture_dir}" || + fail "${fixture_name} rejected the strict signature fixture" + grep -Fx -- "${EXPECTED_IDENTITY_REGEXP}" "${cosign_args}" >/dev/null || + fail "${fixture_name} did not pass the exact signer allowlist to cosign" + grep -Fx -- "${CHECKSUM_CERTIFICATE_OIDC_ISSUER}" "${cosign_args}" >/dev/null || + fail "${fixture_name} did not pin the GitHub Actions OIDC issuer" + + PATH=${ORIGINAL_PATH} + unset COSIGN_ARGS_FILE BORINGCACHE_VERIFY_SIGNATURE + export PATH +} + +trap cleanup EXIT HUP INT TERM + +test_installer "${CLI_ROOT}/install.sh" "root-installer" +test_installer "${CLI_ROOT}/install-web/install.sh" "web-installer" + +printf 'installer trust tests passed\n'