Skip to content

Commit 8a699ac

Browse files
committed
Merge branch 'release/0.28.0'
* release/0.28.0: (#149) Switch to only signing when required (#147) Update cert subject name + set via env var
2 parents 8ed343d + e772355 commit 8a699ac

File tree

7 files changed

+143
-8
lines changed

7 files changed

+143
-8
lines changed

Chocolatey.Cake.Recipe/Content/build.cake

+2-2
Original file line numberDiff line numberDiff line change
@@ -570,9 +570,9 @@ public class Builder
570570
BuildParameters.Tasks.CreateChocolateyPackagesTask.IsDependentOn("Configuration-Builder");
571571
BuildParameters.Tasks.CreateChocolateyPackagesTask.IsDependentOn("Obfuscate-Assemblies");
572572
BuildParameters.Tasks.CreateChocolateyPackagesTask.IsDependentOn("Sign-Assemblies");
573-
BuildParameters.Tasks.CreateNuGetPackagesTask.IsDependentOn("Sign-PowerShellScripts");
573+
BuildParameters.Tasks.CreateNuGetPackagesTask.IsDependentOn("Verify-PowerShellScripts");
574574
BuildParameters.Tasks.CreateNuGetPackagesTask.IsDependentOn("Sign-Assemblies");
575-
BuildParameters.Tasks.CreateChocolateyPackagesTask.IsDependentOn("Sign-PowerShellScripts");
575+
BuildParameters.Tasks.CreateChocolateyPackagesTask.IsDependentOn("Verify-PowerShellScripts");
576576
BuildParameters.Tasks.SignMsisTask.IsDependentOn("Sign-Assemblies");
577577
BuildParameters.Tasks.CreateChocolateyPackagesTask.IsDependentOn(prefix + "Build");
578578
BuildParameters.Tasks.ObfuscateAssembliesTask.IsDependeeOf("Sign-Assemblies");

Chocolatey.Cake.Recipe/Content/parameters.cake

+14-1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ public static class BuildParameters
107107
public static Func<List<PSScriptAnalyzerSettings>> GetPSScriptAnalyzerSettings { get; private set; }
108108
public static Func<FilePathCollection> GetMsisToSign { get; private set; }
109109
public static Func<FilePathCollection> GetProjectsToPack { get; private set; }
110+
public static Func<FilePathCollection> GetScriptsToVerify { get; private set; }
110111
public static Func<FilePathCollection> GetScriptsToSign { get; private set; }
111112
public static GitReleaseManagerCredentials GitReleaseManager { get; private set; }
112113
public static string IntegrationTestAssemblyFilePattern { get; private set; }
@@ -146,6 +147,7 @@ public static class BuildParameters
146147
public static DirectoryPath RootDirectoryPath { get; private set; }
147148
public static bool ShouldAuthenticodeSignMsis { get; private set; }
148149
public static bool ShouldAuthenticodeSignOutputAssemblies { get; private set; }
150+
public static bool ShouldVerifyPowerShellScripts { get; private set; }
149151
public static bool ShouldAuthenticodeSignPowerShellScripts { get; private set; }
150152
public static bool ShouldBuildMsi { get; private set; }
151153
public static bool ShouldBuildNuGetSourcePackage { get; private set; }
@@ -280,6 +282,7 @@ public static class BuildParameters
280282
context.Information("RestorePackagesDirectory: {0}", RestorePackagesDirectory);
281283
context.Information("ShouldAuthenticodeSignMsis: {0}", BuildParameters.ShouldAuthenticodeSignMsis);
282284
context.Information("ShouldAuthenticodeSignOutputAssemblies: {0}", BuildParameters.ShouldAuthenticodeSignOutputAssemblies);
285+
context.Information("ShouldVerifyPowerShellScripts: {0}", BuildParameters.ShouldVerifyPowerShellScripts);
283286
context.Information("ShouldAuthenticodeSignPowerShellScripts: {0}", BuildParameters.ShouldAuthenticodeSignPowerShellScripts);
284287
context.Information("ShouldBuildMsi: {0}", BuildParameters.ShouldBuildMsi);
285288
context.Information("ShouldBuildNuGetSourcePackage: {0}", BuildParameters.ShouldBuildNuGetSourcePackage);
@@ -360,6 +363,7 @@ public static class BuildParameters
360363
Func<List<PSScriptAnalyzerSettings>> getPSScriptAnalyzerSettings = null,
361364
Func<FilePathCollection> getMsisToSign = null,
362365
Func<FilePathCollection> getProjectsToPack = null,
366+
Func<FilePathCollection> getScriptsToVerify = null,
363367
Func<FilePathCollection> getScriptsToSign = null,
364368
string integrationTestAssemblyFilePattern = null,
365369
string integrationTestAssemblyProjectPattern = null,
@@ -391,6 +395,7 @@ public static class BuildParameters
391395
DirectoryPath rootDirectoryPath = null,
392396
bool shouldAuthenticodeSignMsis = true,
393397
bool shouldAuthenticodeSignOutputAssemblies = true,
398+
bool shouldVerifyPowerShellScripts = true,
394399
bool shouldAuthenticodeSignPowerShellScripts = true,
395400
bool shouldBuildMsi = false,
396401
bool shouldBuildNuGetSourcePackage = false,
@@ -477,7 +482,7 @@ public static class BuildParameters
477482
CertificateAlgorithm = context.EnvironmentVariable("CERT_ALGORITHM") ?? "Sha256";
478483
CertificateFilePath = context.EnvironmentVariable("CHOCOLATEY_OFFICIAL_CERT") ?? "";
479484
CertificatePassword = context.EnvironmentVariable("CHOCOLATEY_OFFICIAL_CERT_PASSWORD") ?? "";
480-
CertificateSubjectName = certificateSubjectName ?? "Chocolatey Software, Inc.";
485+
CertificateSubjectName = context.EnvironmentVariable("CHOCOLATEY_OFFICIAL_CERT_SUBJECT_NAME") ?? certificateSubjectName ?? "Chocolatey Software, Inc";
481486
CertificateTimestampUrl = context.EnvironmentVariable("CERT_TIMESTAMP_URL") ?? "http://timestamp.digicert.com";
482487
ChocolateyNupkgGlobbingPattern = chocolateyNupkgGlobbingPattern;
483488
ChocolateyNuspecGlobbingPattern = chocolateyNuspecGlobbingPattern;
@@ -495,6 +500,7 @@ public static class BuildParameters
495500
GetPSScriptAnalyzerSettings = getPSScriptAnalyzerSettings;
496501
GetMsisToSign = getMsisToSign;
497502
GetProjectsToPack = getProjectsToPack;
503+
GetScriptsToVerify = getScriptsToVerify;
498504
GetScriptsToSign = getScriptsToSign;
499505
GitReleaseManager = GetGitReleaseManagerCredentials(context);
500506
IntegrationTestAssemblyFilePattern = integrationTestAssemblyFilePattern ?? "/**/*[tT]ests.[iI]ntegration.dll";
@@ -542,6 +548,13 @@ public static class BuildParameters
542548
ShouldAuthenticodeSignOutputAssemblies = context.Argument<bool>("shouldAuthenticodeSignOutputAssemblies");
543549
}
544550

551+
ShouldVerifyPowerShellScripts = shouldVerifyPowerShellScripts;
552+
553+
if (context.HasArgument("shouldVerifyPowerShellScripts"))
554+
{
555+
ShouldVerifyPowerShellScripts = context.Argument<bool>("shouldVerifyPowerShellScripts");
556+
}
557+
545558
ShouldAuthenticodeSignPowerShellScripts = shouldAuthenticodeSignPowerShellScripts;
546559

547560
if (context.HasArgument("shouldAuthenticodeSignPowerShellScripts"))

Chocolatey.Cake.Recipe/Content/paths.cake

+8-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public class BuildPaths
4949
var chocolateyPackagesOutputDirectory = packagesDirectory + "/Chocolatey";
5050

5151
var dependencyCheckReportsDirectory = buildDirectoryPath + "/DependencyCheckReports";
52+
53+
var signedFilesDirectory = buildDirectoryPath + "/SignedFiles";
5254

5355
// Files
5456
var dotNetFormatOutputFilePath = ((DirectoryPath)testResultsDirectory).CombineWithFilePath("dotnet-format.json");
@@ -84,7 +86,8 @@ public class BuildPaths
8486
chocolateyPackagesOutputDirectory,
8587
packagesDirectory,
8688
environmentSettingsDirectory,
87-
dependencyCheckReportsDirectory
89+
dependencyCheckReportsDirectory,
90+
signedFilesDirectory
8891
);
8992

9093
var buildFiles = new BuildFiles(
@@ -176,6 +179,7 @@ public class BuildDirectories
176179
public DirectoryPath Packages { get; private set; }
177180
public DirectoryPath EnvironmentSettings { get; private set; }
178181
public DirectoryPath DependencyCheckReports { get; private set; }
182+
public DirectoryPath SignedFiles { get; private set; }
179183
public ICollection<DirectoryPath> ToClean { get; private set; }
180184

181185
public BuildDirectories(
@@ -199,7 +203,8 @@ public class BuildDirectories
199203
DirectoryPath chocolateyPackages,
200204
DirectoryPath packages,
201205
DirectoryPath environmentSettings,
202-
DirectoryPath dependencyCheckReports
206+
DirectoryPath dependencyCheckReports,
207+
DirectoryPath signedFiles
203208
)
204209
{
205210
Build = build;
@@ -222,6 +227,7 @@ public class BuildDirectories
222227
ChocolateyPackages = chocolateyPackages;
223228
EnvironmentSettings = environmentSettings;
224229
DependencyCheckReports = dependencyCheckReports;
230+
SignedFiles = signedFiles;
225231
Packages = packages;
226232

227233
ToClean = new[] {

Chocolatey.Cake.Recipe/Content/sign-powershell.ps1

+34-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ Param(
1919
[String[]]
2020
$ScriptsToSign,
2121

22+
[Parameter()]
23+
[String]
24+
$OutputFolder,
25+
2226
[Parameter()]
2327
[String]
2428
$TimeStampServer,
@@ -40,11 +44,38 @@ Param(
4044
$CertificateSubjectName
4145
)
4246

43-
$cert = if ($PSCmdlet.ParameterSetName -eq "File") {
47+
$Cert = if ($PSCmdlet.ParameterSetName -eq "File") {
4448
New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertificatePath, $CertificatePassword)
4549
}
4650
else {
47-
Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -Like "*$CertificateSubjectName*"
51+
Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -Like "*$CertificateSubjectName*" -and
52+
$_.Issuer -match 'DigiCert' -and
53+
$_.NotAfter -ge [datetime]::Now}
4854
}
4955

50-
Set-AuthenticodeSignature -FilePath $ScriptsToSign -Cert $cert -TimestampServer $TimeStampServer -IncludeChain NotRoot -HashAlgorithm $CertificateAlgorithm
56+
if ($Cert) {
57+
$CommonSignParams = @{
58+
'TimestampServer' = $TimeStampServer
59+
'IncludeChain' = 'NotRoot'
60+
'HashAlgorithm' = $CertificateAlgorithm
61+
'Cert' = $Cert
62+
}
63+
64+
foreach ($Script in $ScriptsToSign) {
65+
$ExistingSig = Get-AuthenticodeSignature -FilePath $Script
66+
67+
if ($ExistingSig.Status -ne 'Valid' -or $ExistingSig.SignerCertificate.Issuer -notmatch 'DigiCert' -or $ExistingSig.SignerCertificate.NotAfter -lt [datetime]::Now) {
68+
$NewSig = Set-AuthenticodeSignature -FilePath $Script @CommonSignParams
69+
Write-Host "Script file '$Script' signed with status: $($NewSig.Status)"
70+
71+
if (!(Test-Path -Path $OutputFolder)) {
72+
$null = New-Item -Path $OutputFolder -Type Directory
73+
}
74+
Copy-Item -Path $Script -Destination $OutputFolder
75+
} else {
76+
Write-Host "Script file '$Script' does not need signing, current signature is valid."
77+
}
78+
}
79+
} else {
80+
Write-Warning "Skipping script signing, no currently valid DigiCert issued Authenticode signing certificate matching '$($CertificateSubjectName)' was found."
81+
}

Chocolatey.Cake.Recipe/Content/sign.cake

+38
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,37 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16+
BuildParameters.Tasks.VerifyPowerShellScriptsTask = Task("Verify-PowerShellScripts")
17+
.WithCriteria(() => BuildParameters.ShouldVerifyPowerShellScripts, "Skipping since verifying PowerShell scripts has been disabled")
18+
.Does(() =>
19+
{
20+
if (BuildParameters.GetScriptsToVerify != null)
21+
{
22+
var powerShellVerifyScript = GetFiles("./tools/Chocolatey.Cake.Recipe*/Content/verify-powershell.ps1").FirstOrDefault();
23+
24+
if (powerShellVerifyScript == null)
25+
{
26+
Warning("Unable to find PowerShell verification script, so unable to verify PowerShell scripts.");
27+
return;
28+
}
29+
30+
var scriptsToVerify = new List<string>();
31+
foreach (var filePath in BuildParameters.GetScriptsToVerify())
32+
{
33+
scriptsToVerify.Add(MakeAbsolute(filePath).FullPath);
34+
}
35+
36+
StartPowershellFile(MakeAbsolute(powerShellVerifyScript), args =>
37+
{
38+
args.AppendArray("ScriptsToVerify", scriptsToVerify);
39+
});
40+
}
41+
else
42+
{
43+
Information("There are no PowerShell Scripts defined to be verified.");
44+
}
45+
});
46+
1647
BuildParameters.Tasks.SignPowerShellScriptsTask = Task("Sign-PowerShellScripts")
1748
.WithCriteria(() => (!string.IsNullOrWhiteSpace(BuildParameters.CertificateFilePath) && FileExists(BuildParameters.CertificateFilePath)) || BuildSystem.IsRunningOnTeamCity, "Skipping because unable to find certificate, and not running on TeamCity")
1849
.WithCriteria(() => BuildParameters.ShouldAuthenticodeSignPowerShellScripts, "Skipping since authenticode signing of PowerShell scripts has been disabled")
@@ -39,6 +70,7 @@ BuildParameters.Tasks.SignPowerShellScriptsTask = Task("Sign-PowerShellScripts")
3970
StartPowershellFile(MakeAbsolute(powerShellSigningScript), args =>
4071
{
4172
args.AppendArray("ScriptsToSign", scriptsToSign)
73+
.Append("OutputFolder", BuildParameters.Paths.Directories.SignedFiles.FullPath)
4274
.Append("TimeStampServer", BuildParameters.CertificateTimestampUrl)
4375
.AppendQuoted("CertificateSubjectName", BuildParameters.CertificateSubjectName)
4476
.Append("CertificateAlgorithm", BuildParameters.CertificateAlgorithm);
@@ -53,12 +85,18 @@ BuildParameters.Tasks.SignPowerShellScriptsTask = Task("Sign-PowerShellScripts")
5385
StartPowershellFile(MakeAbsolute(powerShellSigningScript), args =>
5486
{
5587
args.AppendArray("ScriptsToSign", scriptsToSign)
88+
.Append("OutputFolder", BuildParameters.Paths.Directories.SignedFiles.FullPath)
5689
.Append("TimeStampServer", BuildParameters.CertificateTimestampUrl)
5790
.Append("CertificatePath", BuildParameters.CertificateFilePath)
5891
.AppendSecret("CertificatePassword", password)
5992
.Append("CertificateAlgorithm", BuildParameters.CertificateAlgorithm);
6093
});
6194
}
95+
96+
foreach (var signedFile in GetFiles(BuildParameters.Paths.Directories.SignedFiles + "/**/*"))
97+
{
98+
BuildParameters.BuildProvider.UploadArtifact(signedFile);
99+
}
62100
}
63101
else
64102
{

Chocolatey.Cake.Recipe/Content/tasks.cake

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public class BuildTasks
7878
public CakeTaskBuilder StrongNameSignerTask { get; set; }
7979

8080
// Signing Tasks
81+
public CakeTaskBuilder VerifyPowerShellScriptsTask { get; set; }
8182
public CakeTaskBuilder SignPowerShellScriptsTask { get; set; }
8283
public CakeTaskBuilder SignAssembliesTask { get; set; }
8384
public CakeTaskBuilder SignMsisTask { get; set; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright © 2024 Chocolatey Software, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
#
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
[cmdletBinding()]
17+
Param(
18+
[Parameter()]
19+
[String[]]
20+
$ScriptsToVerify
21+
)
22+
23+
$AllScriptsVerified = $true
24+
25+
Write-Output ""
26+
Write-Output "========== Verifying PowerShell Scripts =========="
27+
Write-Output ""
28+
29+
foreach ($ScriptToVerify in $ScriptsToVerify) {
30+
$ExistingSig = Get-AuthenticodeSignature -FilePath $ScriptToVerify
31+
32+
if ($ExistingSig.Status -ne 'Valid' -or $ExistingSig.SignerCertificate.Issuer -notmatch 'DigiCert' -or $ExistingSig.SignerCertificate.NotAfter -lt [datetime]::Now) {
33+
$AllScriptsVerified = $false
34+
Write-Output "Script file '$ScriptToVerify' contains an invalid signature, which must be corrected before build can succeed."
35+
} else {
36+
Write-Output "Script file '$ScriptToVerify' does not need signing, current signature is valid."
37+
}
38+
}
39+
40+
Write-Output ""
41+
Write-Output "========== Verification Complete =========="
42+
Write-Output ""
43+
44+
if ($AllScriptsVerified -eq $false) {
45+
throw "At least one PowerShell script had an invalid signature. Check output for details."
46+
}

0 commit comments

Comments
 (0)