From 1c6d30ffd83117df409cb3a3ecd6c098d3b8d2a3 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 08:01:07 +0000 Subject: [PATCH] test(canary): verify the pinned gitleaks release --- .gitea/workflows/canaries.yml | 7 +- tests/canaries/test_gitleaks_release.py | 85 +++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 tests/canaries/test_gitleaks_release.py diff --git a/.gitea/workflows/canaries.yml b/.gitea/workflows/canaries.yml index 9d064d37..6140d6ba 100644 --- a/.gitea/workflows/canaries.yml +++ b/.gitea/workflows/canaries.yml @@ -2,7 +2,7 @@ # digest, etc.) without coupling every dev push to upstream registry # availability. # -# Opt-in via CLAUDE_BOTTLE_RUN_CANARIES=1 so the same files can be run +# Opt-in via BOT_BOTTLE_RUN_CANARIES=1 so the same files can be run # locally with the same gating. name: canaries @@ -25,4 +25,7 @@ jobs: # No actions/setup-python: canaries are stdlib unittest on the image's # system Python 3.12 (older act_runner mishandles setup-python's PATH). - name: Run canaries - run: python3 -m unittest discover -t . -s tests/canaries -v + run: | + python3 -m scripts.unittest_gate \ + -t . -s tests/canaries -v \ + --minimum-executed 1 --fail-on-skip diff --git a/tests/canaries/test_gitleaks_release.py b/tests/canaries/test_gitleaks_release.py new file mode 100644 index 00000000..31704af0 --- /dev/null +++ b/tests/canaries/test_gitleaks_release.py @@ -0,0 +1,85 @@ +"""Canary: the pinned gitleaks release remains downloadable and executable. + +The gateway Dockerfile verifies this archive during an image build. Repeating +the upstream check weekly keeps registry/release drift out of normal pull +requests while proving that the pinned URL, architecture checksum, archive +shape, and binary still agree. +""" + +from __future__ import annotations + +import hashlib +import os +import platform +import re +import subprocess +import tarfile +import tempfile +import unittest +import urllib.request +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +DOCKERFILE = ROOT / "Dockerfile.gateway" + + +def _docker_arg(text: str, name: str) -> str: + match = re.search(rf"^ARG {re.escape(name)}=(\S+)$", text, re.MULTILINE) + if match is None: + raise AssertionError(f"Dockerfile.gateway has no concrete ARG {name}") + return match.group(1) + + +@unittest.skipUnless( + os.environ.get("BOT_BOTTLE_RUN_CANARIES") == "1", + "canary suite is opt-in; set BOT_BOTTLE_RUN_CANARIES=1 to run", +) +class TestGitleaksRelease(unittest.TestCase): + def test_pinned_archive_checksum_and_binary(self) -> None: + dockerfile = DOCKERFILE.read_text(encoding="utf-8") + version = _docker_arg(dockerfile, "GITLEAKS_VERSION") + machine = platform.machine().lower() + architectures = { + "x86_64": ("linux_x64", "GITLEAKS_SHA256_AMD64"), + "amd64": ("linux_x64", "GITLEAKS_SHA256_AMD64"), + "aarch64": ("linux_arm64", "GITLEAKS_SHA256_ARM64"), + "arm64": ("linux_arm64", "GITLEAKS_SHA256_ARM64"), + } + if machine not in architectures: + self.fail(f"unsupported canary runner architecture: {machine}") + asset, checksum_arg = architectures[machine] + expected_checksum = _docker_arg(dockerfile, checksum_arg) + url = ( + "https://github.com/gitleaks/gitleaks/releases/download/" + f"v{version}/gitleaks_{version}_{asset}.tar.gz" + ) + + with tempfile.TemporaryDirectory(prefix="bot-bottle-gitleaks-canary.") as tmp: + archive = Path(tmp) / "gitleaks.tar.gz" + urllib.request.urlretrieve(url, archive) + self.assertEqual( + expected_checksum, + hashlib.sha256(archive.read_bytes()).hexdigest(), + "the pinned upstream archive no longer matches Dockerfile.gateway", + ) + with tarfile.open(archive, "r:gz") as bundle: + member = bundle.getmember("gitleaks") + source = bundle.extractfile(member) + if source is None: + self.fail("gitleaks archive member is not a regular file") + binary = Path(tmp) / "gitleaks" + binary.write_bytes(source.read()) + binary.chmod(0o755) + result = subprocess.run( + [str(binary), "version"], + capture_output=True, + text=True, + check=False, + ) + self.assertEqual(0, result.returncode, result.stderr) + self.assertIn(version, result.stdout + result.stderr) + + +if __name__ == "__main__": + unittest.main()