From eb2ce029f88b5152a472de3ebbeffb8ac002d695 Mon Sep 17 00:00:00 2001 From: rldyourmnd Date: Thu, 20 Aug 2026 15:06:18 +0500 Subject: [PATCH] fix(module): make pin verification hermetic Signed-off-by: rldyourmnd --- .gds/repository.yaml | 2 +- CHANGELOG.md | 4 +++ catalog/python-execution.yml | 8 +++-- scripts/check_module_verification.py | 37 ++++++++++++++++++++ scripts/validate_all.py | 2 ++ scripts/validate_module.sh | 52 ++++++++++++++++++++++++++++ 6 files changed, 102 insertions(+), 3 deletions(-) create mode 100755 scripts/check_module_verification.py create mode 100755 scripts/validate_module.sh diff --git a/.gds/repository.yaml b/.gds/repository.yaml index 3baa085..1077bc7 100644 --- a/.gds/repository.yaml +++ b/.gds/repository.yaml @@ -46,7 +46,7 @@ git: verification: commands: lint: ["actionlint"] - test: ["python3 -I -B scripts/validate_all.py --tier core"] + test: ["scripts/validate_module.sh"] required: ["lint", "test"] agent: diff --git a/CHANGELOG.md b/CHANGELOG.md index d2e781c..f4bf602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ The project follows Semantic Versioning. ephemeral Incus containers, destroy-after-use lifecycle, and explicit class labels. Amsterdam is documented as a bastion/application host rather than an Actions execution target. +- Made the GDS module verification lane hermetic with checksum-pinned `uv`, + Python 3.13.14, hash-locked dependencies and the repository package launcher, + so a clean consumer checkout can verify the exact pin without ambient Python + packages. ## [0.1.1] - 2026-08-16 diff --git a/catalog/python-execution.yml b/catalog/python-execution.yml index 7099d14..732a084 100644 --- a/catalog/python-execution.yml +++ b/catalog/python-execution.yml @@ -2,7 +2,7 @@ "schema_version": 1, "python": { "major_minor": "3.13", - "subject_count": 61, + "subject_count": 62, "launcher": "scripts/check_python_execution_contract.py", "launcher_prefix": [ ".venv/bin/python", @@ -96,7 +96,6 @@ } }, "invocation_document_exemptions": { - ".gds/repository.yaml": "Canonical GDS repository metadata records a verification command for orchestration; it is not an executable contributor instruction or workflow.", "catalog/scorecard-evidence.yml": "Historical evidence record of a command as it was actually run. Rewriting it to the current launcher form would falsify the receipt." }, "source_classes": { @@ -298,6 +297,7 @@ "check_harden_runner_contract.py", "check_maintenance_report_contract.py", "check_merge_group.py", + "check_module_verification.py", "check_monorepo_routing.py", "check_permissions.py", "check_pinned_actions.py", @@ -422,6 +422,9 @@ "check_merge_group.py": [ "_workflow_yaml" ], + "check_module_verification.py": [ + "_strict_yaml" + ], "check_monorepo_routing.py": [ "_workflow_yaml", "check_python_execution_contract" @@ -551,6 +554,7 @@ "check_harden_runner_contract", "check_maintenance_report_contract", "check_merge_group", + "check_module_verification", "check_monorepo_routing", "check_permissions", "check_pinned_actions", diff --git a/scripts/check_module_verification.py b/scripts/check_module_verification.py new file mode 100755 index 0000000..2f4a55a --- /dev/null +++ b/scripts/check_module_verification.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Keep GDS module verification hermetic and executable from a clean clone.""" +from __future__ import annotations + +from pathlib import Path + +from ci_workflows_tools._strict_yaml import strict_load + +REPO_ROOT = Path(__file__).resolve().parent.parent + + +def check() -> list[str]: + problems: list[str] = [] + anchor = strict_load(REPO_ROOT / ".gds" / "repository.yaml") + commands = ((anchor.get("verification") or {}).get("commands") or {}) + if commands.get("test") != ["scripts/validate_module.sh"]: + problems.append("module test lane must call only scripts/validate_module.sh") + wrapper = REPO_ROOT / "scripts" / "validate_module.sh" + if not wrapper.is_file() or wrapper.is_symlink(): + return problems + ["hermetic module verification wrapper is missing or unsafe"] + text = wrapper.read_text(encoding="utf-8") + required = ( + "UV_VERSION=0.11.30", + 'PYTHON_ENV="$ROOT/.venv"', + "PYTHON_ENV_OWNED=0", + "python find 3.13.14", + "-m venv --copies", + "--require-hashes -r requirements-ci.txt", + "check_python_execution_contract.py", + "--launch validate_all.py -- --tier core", + ) + for marker in required: + if marker not in text: + problems.append(f"module verification wrapper omits {marker!r}") + if "python3 -I -B scripts/validate_all.py" in text: + problems.append("module verification bypasses the repository package launcher") + return problems diff --git a/scripts/validate_all.py b/scripts/validate_all.py index f9f8335..6c9e31b 100644 --- a/scripts/validate_all.py +++ b/scripts/validate_all.py @@ -59,6 +59,7 @@ check_gate_contract, check_harden_runner_contract, check_merge_group, + check_module_verification, check_monorepo_routing, check_permissions, check_pinned_actions, @@ -137,6 +138,7 @@ ("evidence-orchestration", compile_evidence_plan.check), ("side-effect-fixture", check_side_effect_fixture_contract.check), ("merge-group", check_merge_group.check), + ("module-verification", check_module_verification.check), ("rulesets", check_rulesets.check), ("catalog", validate_catalog.check), ("profiles", validate_profiles.check), diff --git a/scripts/validate_module.sh b/scripts/validate_module.sh new file mode 100755 index 0000000..e0bad82 --- /dev/null +++ b/scripts/validate_module.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +UV_VERSION=0.11.30 +UV_SHA256_X64=04bc7d180d6138bf6dc08387acf507a823f397a98fea55da36b0ccc7fbce3b68 +UV_SHA256_ARM64=8c11d90f5f66d232930cf8ae3a085c39877690d409e10878234802b028b20e2a +UV_HOME="" +PYTHON_ENV="$ROOT/.venv" +PYTHON_ENV_OWNED=0 + +cleanup() { + [ "$PYTHON_ENV_OWNED" -eq 1 ] && rm -rf -- "$PYTHON_ENV" + [ -n "$UV_HOME" ] && rm -rf -- "$UV_HOME" + return 0 +} +trap cleanup EXIT INT TERM + +uv_binary=$(command -v uv 2>/dev/null || true) +if [ -z "$uv_binary" ] || [ "$("$uv_binary" --version 2>/dev/null | awk '{print $2}')" != "$UV_VERSION" ]; then + [ "$(uname -s)" = Linux ] || { + printf 'uv %s is required on this platform\n' "$UV_VERSION" >&2 + exit 1 + } + case "$(uname -m)" in + x86_64 | amd64) arch=x86_64; digest=$UV_SHA256_X64 ;; + aarch64 | arm64) arch=aarch64; digest=$UV_SHA256_ARM64 ;; + *) printf 'no pinned uv artifact for %s\n' "$(uname -m)" >&2; exit 1 ;; + esac + UV_HOME=$(mktemp -d "${TMPDIR:-/tmp}/ci-workflows-uv.XXXXXX") + curl -fsSL \ + "https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-${arch}-unknown-linux-gnu.tar.gz" \ + -o "$UV_HOME/uv.tar.gz" + printf '%s %s\n' "$digest" "$UV_HOME/uv.tar.gz" | sha256sum --check --status + tar -xzf "$UV_HOME/uv.tar.gz" --strip-components=1 -C "$UV_HOME" + uv_binary=$UV_HOME/uv +fi + +cd "$ROOT" +if [ ! -e "$PYTHON_ENV" ]; then + python_binary=$("$uv_binary" python find 3.13.14) + "$python_binary" -I -B -m venv --copies "$PYTHON_ENV" + PYTHON_ENV_OWNED=1 +fi +[ -d "$PYTHON_ENV" ] && [ ! -L "$PYTHON_ENV" ] || { + printf '%s must be a real directory\n' "$PYTHON_ENV" >&2 + exit 1 +} +"$uv_binary" pip install --python "$PYTHON_ENV/bin/python" \ + --require-hashes -r requirements-ci.txt +"$PYTHON_ENV/bin/python" -I -B scripts/check_python_execution_contract.py \ + --launch validate_all.py -- --tier core