Skip to content

Commit 227b5ed

Browse files
committed
verify-action-build: recognize fileSHA256()-style file-hash helpers as verification
opentofu/setup-opentofu's `lib/setup-tofu.js` downloads the CLI zip via `tc.downloadTool()` and then validates it: const checksum = await fileSHA256(pathToCLIZip); if (!checksums.includes(checksum)) throw new Error(...); // SHA256SUMS `fileSHA256` is imported from the sibling `lib/util.js`, where the actual `createHash('sha256')` body lives. Within the downloader file the only verification evidence is the `fileSHA256(...)` call name — but none of the `_JS_VERIFICATION_PATTERNS` matched that shape, so the v2.0.1 -> v2.0.2 bump (#980) was false-flagged as an unverified download and the `verify` job failed. This is the same class as the astral-sh/setup-uv `validateChecksum()` fix (#910/#916): the hashing implementation lives in a sibling module and the call name is the surviving in-file evidence. Add a pattern that recognizes a helper call whose name contains an explicit SHA algorithm (`fileSHA256(`, `getSHA512(`, `sha256File(`, ...). It stays tight enough not to match `createHash('sha256')` (the paren precedes the algorithm there) or non-SHA hashes. Covered by two regression tests: the bare helper-name family and a faithful trim of the real opentofu download->verify sequence. Generated-by: Claude Code (Opus 4.8)
1 parent 762de79 commit 227b5ed

2 files changed

Lines changed: 47 additions & 0 deletions

File tree

utils/tests/verify_action_build/test_security.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,43 @@ def test_verify_hash_function_recognized(self):
13601360
):
13611361
assert self._has_verification(snippet) is True, snippet
13621362

1363+
def test_file_sha_helper_name_recognized(self):
1364+
# ``fileSHA256(path)`` shape: a helper whose name denotes hashing a
1365+
# file with an explicit SHA algorithm. Several actions define the
1366+
# ``createHash`` body in a sibling util module, so the call name is
1367+
# the in-file verification evidence.
1368+
for func_name in (
1369+
"fileSHA256",
1370+
"fileSHA512",
1371+
"getSHA256",
1372+
"sha256File",
1373+
):
1374+
content = f"const checksum = await {func_name}(pathToCLIZip)\n"
1375+
assert self._has_verification(content) is True, func_name
1376+
1377+
def test_file_sha_helper_real_setup_opentofu_snippet_recognized(self):
1378+
# Faithful trim of opentofu/setup-opentofu@v2.0.2's real download →
1379+
# verify sequence in ``lib/setup-tofu.js``. ``fileSHA256`` is imported
1380+
# from the sibling ``./util.js`` (where the ``createHash('sha256')``
1381+
# body lives), and the artifact is rejected when its hash isn't in the
1382+
# release's ``SHA256SUMS`` list. Without recognizing the call name the
1383+
# v2.0.2 bump (#980) false-flagged this download as unverified.
1384+
content = """\
1385+
import { downloadTool, extractZip } from '@actions/tool-cache';
1386+
import { fileSHA256 } from './util.js';
1387+
1388+
async function downloadAndExtractCLI (url, checksums) {
1389+
const pathToCLIZip = await downloadTool(url);
1390+
if (checksums.length > 0) {
1391+
const checksum = await fileSHA256(pathToCLIZip);
1392+
if (!checksums.includes(checksum)) {
1393+
throw new Error(`Failed to validate OpenTofu CLI zip checksum.`);
1394+
}
1395+
}
1396+
}
1397+
"""
1398+
assert self._has_verification(content) is True
1399+
13631400
def test_existing_crypto_dotted_form_still_matches(self):
13641401
# Regression: the original ``crypto\.createHash\(`` matcher
13651402
# must still fire — existing actions that use the dotted form.

utils/verify_action_build/security.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,16 @@ def analyze_action_metadata(
12201220
r"\bvalidate(?:Checksum|Hash|Digest|SHA\d*|FileChecksum)\b",
12211221
re.IGNORECASE,
12221222
),
1223+
# Helper call whose name denotes hashing a file with an explicit SHA
1224+
# algorithm — e.g. ``fileSHA256(path)``, ``getSHA512(...)``. As with the
1225+
# ``validate*`` helpers above, the ``createHash`` implementation often
1226+
# lives in a sibling util module, so within the downloader file the call
1227+
# name is the surviving verification evidence. opentofu/setup-opentofu's
1228+
# ``lib/setup-tofu.js`` computes ``await fileSHA256(pathToCLIZip)`` (the
1229+
# ``createHash('sha256')`` body is in ``lib/util.js``) and throws when the
1230+
# hash isn't in the release's ``SHA256SUMS`` list; without this the v2.0.2
1231+
# bump (#980) false-flagged the download as unverified.
1232+
re.compile(r"\b\w*SHA(?:1|256|384|512)\w*\s*\(", re.IGNORECASE),
12231233
]
12241234

12251235
_JS_SOURCE_EXTENSIONS = (".ts", ".js", ".mjs", ".cjs")

0 commit comments

Comments
 (0)