diff --git a/provider-ci/internal/pkg/templates/base/scripts/crossbuild.mk b/provider-ci/internal/pkg/templates/base/scripts/crossbuild.mk index 83d5cb3ab8..ecc2d794f6 100644 --- a/provider-ci/internal/pkg/templates/base/scripts/crossbuild.mk +++ b/provider-ci/internal/pkg/templates/base/scripts/crossbuild.mk @@ -59,6 +59,12 @@ bin/%/$(PROVIDER) bin/%/$(PROVIDER).exe: bin/jsign-7.4.jar fi; \ fi + @# Verify the windows binary was actually signed when signing was expected. + @# This runs in its own shell as a separate recipe line, so make checks its + @# exit code directly (no set-e-inside-if antipattern). Catches the silent + @# failure where the signing block above exits 0 without producing a signature. + @[ "${GOOS}" != "windows" ] || [ "${SKIP_SIGNING}" = "true" ] || python3 scripts/verify_signed.py "$@" + bin/jsign-7.4.jar: wget https://github.com/ebourg/jsign/releases/download/7.4/jsign-7.4.jar --output-document=bin/jsign-7.4.jar diff --git a/provider-ci/internal/pkg/templates/base/scripts/verify_signed.py b/provider-ci/internal/pkg/templates/base/scripts/verify_signed.py new file mode 100644 index 0000000000..06816dfabe --- /dev/null +++ b/provider-ci/internal/pkg/templates/base/scripts/verify_signed.py @@ -0,0 +1,51 @@ +"""Verify a Windows PE binary has an Authenticode signature attached. + +Presence check only: parses the PE Optional Header's Certificate Table +directory entry and exits 0 if it is non-empty, non-zero otherwise. Does +not validate the certificate chain or the signature itself; full chain +validation is the verify-release workflow's job. + +Purpose: catch the silent-failure pattern in scripts/crossbuild.mk where +the signing block exits 0 without producing a signature (e.g. a future +regression that returns the recipe to /bin/sh and silently skips the +bash conditional). + +Usage: python3 scripts/verify_signed.py +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from(" +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from(" +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from(" +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from(" +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from(" +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from(" +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from(" +""" + +import struct +import sys + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print( + f"usage: {argv[0]} ", + file=sys.stderr, + ) + return 2 + path = argv[1] + with open(path, "rb") as f: + data = f.read() + + # PE header offset is stored at 0x3C. + pe = struct.unpack_from("