From d17c15e4252b2212cdbe0d870bad2ff6b2e19fef Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Fri, 5 Jun 2026 02:32:58 +0200 Subject: [PATCH] verify: accept validate* checksum helpers as JS download verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit astral-sh/setup-uv@v8.2.0 refactored its uv-binary checksum validation out of src/download/download-version.ts into a sibling module (src/download/checksum/checksum.ts). The download site still calls validateChecksum(checksum, downloadPath, …) immediately after tc.downloadTool, but the same-file JS verification heuristic only recognised verify*/compute*/calculate* helper names — so it lost the createHash token and false-flagged the download as unverified, blocking the routine setup-uv bump in #910. Add the validate* checksum-helper family to the JS verification markers, plus a regression test anchored to the real setup-uv source. Generated-by: Claude Opus 4.8 (1M context) --- .../verify_action_build/test_security.py | 36 +++++++++++++++++++ utils/verify_action_build/security.py | 13 +++++++ 2 files changed, 49 insertions(+) diff --git a/utils/tests/verify_action_build/test_security.py b/utils/tests/verify_action_build/test_security.py index 0916653fc..e8094def1 100644 --- a/utils/tests/verify_action_build/test_security.py +++ b/utils/tests/verify_action_build/test_security.py @@ -1283,6 +1283,42 @@ def test_calculate_sha256_function_name_recognized(self): content = f"const sha = {func_name}(downloadPath)\n" assert self._has_verification(content) is True, func_name + def test_validate_checksum_function_name_recognized(self): + # astral-sh/setup-uv@v8.2.0 shape: ``src/download/download-version.ts`` + # downloads via ``tc.downloadTool`` then calls ``validateChecksum(...)``, + # whose implementation was extracted into a sibling module + # (``./checksum/checksum``). The call name is the in-file evidence the + # scanner must accept; without it, #910 false-flagged the download as + # unverified. + for func_name in ( + "validateChecksum", + "validateFileCheckSum", + "validateHash", + "validateDigest", + "validateSHA256", + ): + content = ( + "const downloadPath = await tc.downloadTool(url)\n" + f"await {func_name}(checksum, downloadPath, arch, platform, version)\n" + ) + assert self._has_verification(content) is True, func_name + + def test_validate_checksum_real_setup_uv_snippet_recognized(self): + # Faithful trim of the real download → validate sequence so the + # regression is anchored to the actual source, not just the bare name. + content = """\ +import * as tc from "@actions/tool-cache"; +import { validateChecksum } from "./checksum/checksum"; + +const downloadPath = await tc.downloadTool( + downloadUrl, + undefined, + githubToken, +); +await validateChecksum(checksum, downloadPath, arch, platform, version); +""" + assert self._has_verification(content) is True + def test_verify_hash_function_recognized(self): # Whether named ``verifyHash`` or referenced inline. for snippet in ( diff --git a/utils/verify_action_build/security.py b/utils/verify_action_build/security.py index 29a334f8a..2f07b5d6d 100644 --- a/utils/verify_action_build/security.py +++ b/utils/verify_action_build/security.py @@ -1207,6 +1207,19 @@ def analyze_action_metadata( re.compile(r"\bcalculate(?:SHA[\d]+|Checksum|Digest)\b", re.IGNORECASE), re.compile(r"\bverifyHash\b"), re.compile(r"\bcomputeChecksum\b"), + # ``validate*`` checksum helpers — the call site counts as verification + # even when the helper's implementation lives in a sibling module. + # astral-sh/setup-uv's ``src/download/download-version.ts`` downloads via + # ``tc.downloadTool`` then immediately calls ``validateChecksum(checksum, + # downloadPath, …)`` (imported from ``./checksum/checksum``), which SHA-256s + # the artifact against a provided checksum or the built-in KNOWN_CHECKSUMS + # table. v8.2.0 extracted that validation into the sibling module, moving + # the ``createHash`` token out of this file and tripping the same-file + # heuristic — the call name is the evidence that survives the refactor. + re.compile( + r"\bvalidate(?:Checksum|Hash|Digest|SHA\d*|FileChecksum)\b", + re.IGNORECASE, + ), ] _JS_SOURCE_EXTENSIONS = (".ts", ".js", ".mjs", ".cjs")