diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index bf93a623..96029e03 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -324,8 +324,6 @@ jobs: cat desktop-windows-artifacts.sha256 - name: Attest Windows release artifacts - # Keep uploads/verifiers running if GitHub token policy rejects artifact attestation. - continue-on-error: true uses: actions/attest@281a49d4cbb0a72c9575a50d18f6deb515a11deb # was v4 with: subject-checksums: desktop-windows-artifacts.sha256 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b5bac14..eb0baacb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -271,8 +271,6 @@ jobs: cat windows-release-artifacts.sha256 - name: Attest Windows release artifacts - # Keep release uploads running if GitHub token policy rejects artifact attestation. - continue-on-error: true uses: actions/attest@281a49d4cbb0a72c9575a50d18f6deb515a11deb # was v4 with: subject-checksums: windows-release-artifacts.sha256 diff --git a/scripts/ci/_common.sh b/scripts/ci/_common.sh index 4fbe9617..1c2dc81c 100755 --- a/scripts/ci/_common.sh +++ b/scripts/ci/_common.sh @@ -3670,9 +3670,9 @@ verify_windows_authenticode_signatures() { $issuer = $signature.SignerCertificate.Issuer } if ($subject -ne $expectedSubject) { - throw "Authenticode signer subject mismatch for $file. Expected=$expectedSubject Actual=$subject Issuer=$issuer" + throw "Authenticode signer subject mismatch for $file. Actual=$subject Issuer=$issuer" } - Write-Host ("verified-windows-authenticode {0} subject={1} issuer={2}" -f $file, $subject, $issuer) + Write-Host ("verified-windows-authenticode {0} thumbprint={1}" -f $file, $signature.SignerCertificate.Thumbprint) } ' "${ps_args[@]}" } diff --git a/scripts/ci/attestation-manifest.sh b/scripts/ci/attestation-manifest.sh index 2c5aea3d..11d423be 100755 --- a/scripts/ci/attestation-manifest.sh +++ b/scripts/ci/attestation-manifest.sh @@ -48,4 +48,14 @@ if [ "${#proof_files[@]}" -gt 0 ]; then done >> "${out}" fi +# actions/attest@v4 splits checksum files on the runner platform EOL. +# Windows runners need CRLF here even when Git Bash generated the file. +case "$(host_os)" in + mingw* | msys* | cygwin*) + tmp="$(mktemp)" + awk '{ sub(/\r$/, ""); printf "%s\r\n", $0 }' "${out}" > "${tmp}" + mv "${tmp}" "${out}" + ;; +esac + cat "${out}" diff --git a/scripts/ci/verify-release-artifacts.sh b/scripts/ci/verify-release-artifacts.sh index fc98a732..1994af2e 100755 --- a/scripts/ci/verify-release-artifacts.sh +++ b/scripts/ci/verify-release-artifacts.sh @@ -125,6 +125,65 @@ verify_file_manifest() { done < "${manifest}" } +verify_windows_runtime_manifest() { + local manifest="$1" + local digest label path expected_path + local count=0 + local expected_paths=( + "frontend/src-tauri/resources/windows/MSVCP140.dll" + "frontend/src-tauri/resources/windows/MSVCP140_1.dll" + "frontend/src-tauri/resources/windows/onnxruntime.dll" + "frontend/src-tauri/resources/windows/VCRUNTIME140.dll" + "frontend/src-tauri/resources/windows/VCRUNTIME140_1.dll" + ) + declare -A seen=() + + for expected_path in "${expected_paths[@]}"; do + seen["${expected_path}"]=0 + done + + while read -r digest label _; do + [ -n "${digest:-}" ] || continue + + if ! [[ "${digest}" =~ ^[0-9a-fA-F]{64}$ ]]; then + echo "Invalid Windows runtime manifest line in ${manifest}: ${digest} ${label:-}" >&2 + return 1 + fi + + if [ -z "${label:-}" ]; then + echo "Missing Windows runtime manifest label in ${manifest}." >&2 + return 1 + fi + + path="$(manifest_label_path "${label}")" + if [ -z "${seen[${path}]+x}" ]; then + echo "Unexpected Windows runtime DLL proof in ${manifest}: ${path}" >&2 + return 1 + fi + + if [ "${seen[${path}]}" = "1" ]; then + echo "Duplicate Windows runtime DLL proof in ${manifest}: ${path}" >&2 + return 1 + fi + + seen["${path}"]=1 + count=$((count + 1)) + printf 'verified-windows-runtime-dll-proof %s %s\n' "${digest}" "${label}" + done < "${manifest}" + + for expected_path in "${expected_paths[@]}"; do + if [ "${seen[${expected_path}]}" != "1" ]; then + echo "Missing Windows runtime DLL proof in ${manifest}: ${expected_path}" >&2 + return 1 + fi + done + + if [ "${count}" -ne "${#expected_paths[@]}" ]; then + echo "Unexpected Windows runtime DLL proof count in ${manifest}: ${count}" >&2 + return 1 + fi +} + verify_proof_file_hash() { local manifest="$1" local digest label file actual @@ -360,7 +419,7 @@ verify_windows() { verify_file_manifest "${final_manifest}" if [ -n "${runtime_manifest}" ]; then - verify_file_manifest "${runtime_manifest}" + verify_windows_runtime_manifest "${runtime_manifest}" fi verify_tauri_signatures_in_artifacts }