-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathverify-windows-authenticode.ps1
More file actions
126 lines (107 loc) · 3.98 KB
/
Copy pathverify-windows-authenticode.ps1
File metadata and controls
126 lines (107 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# Verify Authenticode signatures on Windows PE files (installer, app, uninstaller).
#
# Modes:
# optional — report status; unsigned/NotSigned exits 0 (current milestone while
# OV/EV cert budget is not in place). Fail only if a path is missing
# or Get-AuthenticodeSignature itself errors.
# required — every path must have Status -eq Valid. Use after cert is wired
# into release (repo var AUTHENTICODE_REQUIRED=true).
#
# Usage:
# pwsh scripts/verify-windows-authenticode.ps1 -Path a.exe,b.exe -Mode optional
# pwsh scripts/verify-windows-authenticode.ps1 -Path (Get-ChildItem *.exe) -Mode required
#
# Does NOT check Tauri updater (.sig / latest.json) signatures — that is a
# separate system (see docs/windows-signing.md).
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string[]]$Path,
[ValidateSet("optional", "required")]
[string]$Mode = "optional",
# When set (required mode), SignerCertificate.Subject must contain this.
[string]$ExpectedSubject = "",
[string]$Stage = "sign-verify"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
function Write-Stage([string]$Message) {
Write-Host "::group::[$Stage] $Message"
}
function Close-Stage {
Write-Host "::endgroup::"
}
function Fail-Stage([string]$Message) {
Write-Host "::error::[$Stage] $Message"
throw "[$Stage] $Message"
}
Write-Stage "Authenticode verification (mode=$Mode)"
$results = @()
$failed = $false
foreach ($raw in $Path) {
if ([string]::IsNullOrWhiteSpace($raw)) { continue }
$item = Get-Item -LiteralPath $raw -ErrorAction SilentlyContinue
if (-not $item) {
$failed = $true
Write-Host "::error::[$Stage] missing file: $raw"
$results += [pscustomobject]@{
Path = $raw
Status = "Missing"
Subject = ""
Ok = $false
}
continue
}
$sig = Get-AuthenticodeSignature -LiteralPath $item.FullName
$subject = if ($sig.SignerCertificate) { $sig.SignerCertificate.Subject } else { "" }
$status = [string]$sig.Status
$ok = $false
switch ($Mode) {
"optional" {
# NotSigned is expected until OV/EV is configured. HashMismatch /
# NotTrusted / UnknownError still surface as warnings for visibility.
if ($status -eq "Valid") {
$ok = $true
}
elseif ($status -eq "NotSigned") {
$ok = $true
Write-Host "::warning::[$Stage] unsigned (expected until Authenticode cert is configured): $($item.Name)"
}
else {
# Soft-fail in optional mode: report but do not block release.
$ok = $true
Write-Host "::warning::[$Stage] $($item.Name): Status=$status Subject=$subject"
}
}
"required" {
if ($status -ne "Valid") {
$ok = $false
Write-Host "::error::[$Stage] $($item.Name): expected Valid, got $status"
}
elseif ($ExpectedSubject -and ($subject -notlike "*$ExpectedSubject*")) {
$ok = $false
Write-Host "::error::[$Stage] $($item.Name): subject '$subject' does not contain '$ExpectedSubject'"
}
else {
$ok = $true
}
}
}
if (-not $ok) { $failed = $true }
$results += [pscustomobject]@{
Path = $item.FullName
Status = $status
Subject = $subject
Ok = $ok
}
Write-Host (" {0,-12} {1} {2}" -f $status, $item.Name, $subject)
}
$results | Format-Table -AutoSize | Out-String | Write-Host
Close-Stage
if ($failed) {
Fail-Stage "Authenticode verification failed (mode=$Mode)"
}
Write-Host "[$Stage] Authenticode check passed (mode=$Mode, files=$($results.Count))"
# Do not `exit` — scripts are invoked in-process with `&` from CI steps;
# `exit` would terminate the whole step (and skip e.g. smoke after verify).
return