diff --git a/utils/tests/verify_action_build/test_security.py b/utils/tests/verify_action_build/test_security.py index e8094def..9c8e4d37 100644 --- a/utils/tests/verify_action_build/test_security.py +++ b/utils/tests/verify_action_build/test_security.py @@ -1405,6 +1405,22 @@ def test_licenses_txt_exempt(self): assert _looks_like_in_tree_binary("dist/licenses.txt") is False assert _looks_like_in_tree_binary("licenses.txt") is False + def test_gradle_wrapper_jar_exempt(self): + # gradle/wrapper/gradle-wrapper.jar is Gradle's build wrapper: + # build-time tooling of the upstream repo, never executed on a + # consumer's runner, and checksum-verifiable in its own right. + # JetBrains/qodana-action (a node24 action) ships it and was + # false-flagged before this exemption. + assert _looks_like_in_tree_binary("gradle/wrapper/gradle-wrapper.jar") is False + # Exempt under a nested action sub-path too. + assert _looks_like_in_tree_binary( + "subdir/gradle/wrapper/gradle-wrapper.jar" + ) is False + # Precision: a stray jar of the same name elsewhere is still caught, + # as is any other .jar (the suffix match needs the canonical path). + assert _looks_like_in_tree_binary("dist/gradle-wrapper.jar") is True + assert _looks_like_in_tree_binary("Library.jar") is True + def test_matlab_platform_dir_naming(self): # MATLAB's launcher convention: dist/bin//run-matlab-command # where is MATLAB's own arch identifier and the file has diff --git a/utils/verify_action_build/security.py b/utils/verify_action_build/security.py index 2f07b5d6..e60b6851 100644 --- a/utils/verify_action_build/security.py +++ b/utils/verify_action_build/security.py @@ -1694,6 +1694,21 @@ def analyze_repo_metadata( "licenses.txt", } +# Repo-relative path suffixes for binaries that are build-time tooling of +# the *upstream* repo rather than action-runtime code, and are themselves +# independently verifiable — so flagging them as opaque runtime binaries +# is a false positive. Matched by full canonical suffix (not bare name) +# so a same-named blob dropped elsewhere is still caught. +_IN_TREE_BINARY_EXEMPT_PATH_SUFFIXES = ( + # Gradle's build wrapper, committed to virtually every Gradle project. + # It is invoked by ``./gradlew`` during the upstream repo's own build — + # never executed on a consumer's runner when the action runs — and is + # checksum-verifiable against Gradle's published list (cf. + # gradle/wrapper-validation-action). Seen on JetBrains/qodana-action, + # a node24 action that never shells out to Gradle at runtime. + "gradle/wrapper/gradle-wrapper.jar", +) + def _looks_like_in_tree_binary(path: str) -> bool: """Return True if ``path`` (a repo-relative blob path) looks like a @@ -1707,6 +1722,8 @@ def _looks_like_in_tree_binary(path: str) -> bool: name = path.rsplit("/", 1)[-1] if name in _IN_TREE_BINARY_EXEMPT_NAMES: return False + if path.lower().endswith(_IN_TREE_BINARY_EXEMPT_PATH_SUFFIXES): + return False lower = name.lower() if lower.endswith(_IN_TREE_BINARY_EXTENSIONS): return True