Skip to content

Commit 57c22c5

Browse files
authored
Generate test certificates in pipeline (#4747)
The tests use test certificates for codesigning and for HTTPS with the localhost server. These test certificates were stored as secure files in the pipelines. This PR changes the test pipeline to generate the certificates on each run so that we don't need to create new certificates when they expire, or if we want to recreate the pipelines in a different ADO account. * Added pipeline steps to create/install the test certificates for codesigning and HTTPS. The scripts export the path and password (a GUID) as pipeline variables for use in subsequent tasks * Removed unnecessary BinSkim task (already included in official pipelines) * Removed unused certificates from the code (unrelated to the ones being replaced) * Updated hash tests that were using the existing certificates to use a different file, as the cert is no longer constant * Added collection of the logs from the localhost server and index creation; this requires stopping the server after it is used
1 parent d010e24 commit 57c22c5

File tree

12 files changed

+72
-79
lines changed

12 files changed

+72
-79
lines changed

.github/actions/spelling/expect.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ appname
3030
appshutdown
3131
APPTERMINATION
3232
archs
33-
argumentlist
3433
ARMNT
3534
arp
3635
arphelper
@@ -390,7 +389,6 @@ Peet
390389
peetdev
391390
PEGI
392391
pfn
393-
pfxpath
394392
pgp
395393
Pherson
396394
pid
@@ -469,7 +467,6 @@ savepoint
469467
schematab
470468
Scm
471469
sddl
472-
SECUREFILEPATH
473470
secureobject
474471
securestring
475472
seof

azure-pipelines.yml

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ jobs:
259259
- template: templates/e2e-setup.yml
260260
parameters:
261261
sourceDir: $(Build.SourcesDirectory)
262-
localhostWebServerArgs: '-BuildRoot $(artifactsDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(Agent.TempDirectory)\TestLocalIndex -LocalSourceJson $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\localsource.json -TestDataPath $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData -SourceCert $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTest.cer -ExitBeforeRun'
262+
localhostWebServerArgs: '-BuildRoot $(artifactsDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(Agent.TempDirectory)\TestLocalIndex -LocalSourceJson $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\localsource.json -TestDataPath $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData -ExitBeforeRun'
263+
signingCertOutDir: $(artifactsDir)\E2ETests
263264

264265
- task: CopyFiles@2
265266
displayName: 'Copy TestLocalIndex'
@@ -302,28 +303,6 @@ jobs:
302303
verbosity: 'Verbose'
303304
alertWarningLevel: 'High'
304305

305-
# Run BimSkim for all the binaries
306-
- task: BinSkim@4
307-
displayName: 'Run BinSkim '
308-
inputs:
309-
arguments: 'analyze
310-
"$(buildOutDir)\AppInstallerCLI\winget.exe"
311-
"$(buildOutDir)\WinGetUtil\WinGetUtil.dll"
312-
"$(buildOutDir)\WindowsPackageManager\WindowsPackageManager.dll"
313-
"$(buildOutDir)\Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.InProc.dll"
314-
"$(Build.SourcesDirectory)\src\WinGetUtilInterop\bin\WinGetUtil*Interop.dll"
315-
"$(buildOutDir)\UndockedRegFreeWinRT\winrtact.dll"
316-
"$(buildOutDir)\Microsoft.WinGet.Client.Cmdlets\Microsoft.WinGet.Client*.dll"
317-
"$(buildOutDir)\ConfigurationRemotingServer\ConfigurationRemoting*Server.dll"
318-
"$(buildOutDir)\ConfigurationRemotingServer\ConfigurationRemoting*Server.exe"
319-
"$(buildOutDir)\ConfigurationRemotingServer\Microsoft.Management.Configuration*.dll"
320-
"$(buildOutDir)\Microsoft.Management.Configuration\Microsoft.Management.Configuration*.dll"
321-
"$(buildOutDir)\Microsoft.Management.Configuration.OutOfProc\Microsoft.Management.Configuration*.dll"
322-
--config default --recurse'
323-
324-
- task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@3
325-
displayName: 'Publish Security Analysis Logs'
326-
327306
# Test job runs tests using build artifacts
328307

329308
- job: 'Test'
@@ -431,7 +410,7 @@ jobs:
431410
- template: templates/e2e-setup.yml
432411
parameters:
433412
sourceDir: $(Build.SourcesDirectory)
434-
localhostWebServerArgs: '-BuildRoot $(buildOutDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(buildOutDir)\E2ETests\TestLocalIndex -SourceCert $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTest.cer'
413+
localhostWebServerArgs: '-BuildRoot $(buildOutDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(buildOutDir)\E2ETests\TestLocalIndex -SourceCert $(buildOutDir)\E2ETests\TestSigningCert.cer'
435414

436415
- template: templates/e2e-test.template.yml
437416
parameters:
@@ -511,6 +490,10 @@ jobs:
511490
arguments: '-TargetLocation $(artifactsDir)\ConfigOOPTestsLog'
512491
condition: succeededOrFailed()
513492

493+
- powershell: Get-Process LocalhostWebServer | Stop-Process
494+
displayName: Stop LocalhostWebServer
495+
condition: succeededOrFailed()
496+
514497
- task: PublishPipelineArtifact@1
515498
displayName: Publish Pipeline Artifacts
516499
inputs:
@@ -524,6 +507,8 @@ jobs:
524507
timeoutInMinutes: 120
525508
dependsOn: 'Build'
526509
condition: succeeded('Build')
510+
variables:
511+
buildOutDir: $(Pipeline.Workspace)\Build.x64Release
527512

528513
steps:
529514
- task: DownloadPipelineArtifact@2
@@ -532,7 +517,7 @@ jobs:
532517
- task: CopyFiles@2
533518
displayName: 'Copy x64 PowerShell Binaries to Output'
534519
inputs:
535-
SourceFolder: '$(Pipeline.Workspace)\Build.x64release\PowerShell'
520+
SourceFolder: '$(buildOutDir)\PowerShell'
536521
Contents: '**\*'
537522
TargetFolder: '$(Build.ArtifactStagingDirectory)'
538523

@@ -566,14 +551,14 @@ jobs:
566551
targetType: 'inline'
567552
script: |
568553
Get-ChildItem AppxPackages\AppInstallerCLIPackage_0.0.2.0_Test\Dependencies\x64 -Filter *.appx | %{ Add-AppxPackage $_.FullName }
569-
workingDirectory: $(Pipeline.Workspace)\Build.x64release\
554+
workingDirectory: $(buildOutDir)
570555

571556
- template: templates/e2e-setup.yml
572557
parameters:
573558
sourceDir: $(Build.SourcesDirectory)
574-
localhostWebServerArgs: '-BuildRoot $(Pipeline.Workspace)\Build.x64release\E2ETests\LocalhostWebServer -StaticFileRoot $(Pipeline.Workspace)\Build.x64release\E2ETests\TestLocalIndex -SourceCert $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTest.cer'
559+
localhostWebServerArgs: '-BuildRoot $(buildOutDir)\E2ETests\LocalhostWebServer -StaticFileRoot $(buildOutDir)\E2ETests\TestLocalIndex -SourceCert $(buildOutDir)\E2ETests\TestSigningCert.cer'
575560

576-
- pwsh: .\RunTests.ps1 -testModulesPath $(Build.ArtifactStagingDirectory) -outputPath $(Pipeline.Workspace)\PesterTest -packageLayoutPath $(Pipeline.Workspace)\Build.x64release\DevPackage
561+
- pwsh: .\RunTests.ps1 -testModulesPath $(Build.ArtifactStagingDirectory) -outputPath $(Pipeline.Workspace)\PesterTest -packageLayoutPath $(buildOutDir)\DevPackage
577562
workingDirectory: $(Build.SourcesDirectory)\src\PowerShell\tests\
578563
displayName: Run PowerShell 7 Tests
579564

@@ -582,6 +567,10 @@ jobs:
582567
displayName: Run Windows PowerShell Tests
583568
condition: succeededOrFailed()
584569

570+
- powershell: Get-Process LocalhostWebServer | Stop-Process
571+
displayName: Stop LocalhostWebServer
572+
condition: succeededOrFailed()
573+
585574
- task: PublishTestResults@2
586575
displayName: Publish Pester Test Results PowerShell 7
587576
inputs:

src/AppInstallerCLIE2ETests/Constants.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ public class Constants
4545
public const string TestSourceType = "Microsoft.PreIndexed.Package";
4646
public const string TestSourceIdentifier = @"WingetE2E.Tests_8wekyb3d8bbwe";
4747

48-
public const string AppInstallerTestCert = "AppInstallerTest.cer";
49-
public const string AppInstallerTestCertThumbprint = "d03e7a688b388b1edde8476a627531c49db88017";
50-
5148
public const string AICLIPackageFamilyName = "WinGetDevCLI_8wekyb3d8bbwe";
5249
public const string AICLIPackageName = "WinGetDevCLI";
5350
public const string AICLIPackagePublisherHash = "8wekyb3d8bbwe";

src/AppInstallerCLIE2ETests/HashCommand.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ public class HashCommand : BaseCommand
2121
[Test]
2222
public void HashFile()
2323
{
24-
var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("AppInstallerTest.cer"));
24+
var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("AppInstallerTestMsiInstaller.msi"));
2525
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
26-
Assert.True(result.StdOut.Contains("9b4c49ad7e47afd97d2e666e93347745e1647c55f1a7ebba6d31b7dd5f69ee68"));
26+
Assert.True(result.StdOut.Contains("21d90ee9b3569590c624836ef50bf39791c7184869c227eedc00585e1f39b4de"));
2727
}
2828

2929
/// <summary>
@@ -44,9 +44,9 @@ public void HashMSIX()
4444
[Test]
4545
public void HashInvalidMSIX()
4646
{
47-
var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("AppInstallerTest.cer") + " -m");
47+
var result = TestCommon.RunAICLICommand("hash", TestCommon.GetTestDataFile("AppInstallerTestMsiInstaller.msi") + " -m");
4848
Assert.AreEqual(Constants.ErrorCode.OPC_E_ZIP_MISSING_END_OF_CENTRAL_DIRECTORY, result.ExitCode);
49-
Assert.True(result.StdOut.Contains("9b4c49ad7e47afd97d2e666e93347745e1647c55f1a7ebba6d31b7dd5f69ee68"));
49+
Assert.True(result.StdOut.Contains("21d90ee9b3569590c624836ef50bf39791c7184869c227eedc00585e1f39b4de"));
5050
Assert.True(result.StdOut.Contains("Please verify that the input file is a valid, signed MSIX."));
5151
}
5252

src/AppInstallerCLIE2ETests/SetUpFixture.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ public void Setup()
4141

4242
shouldRevertDefaultFileTypeRiskOnExit = this.DecreaseFileTypeRisk(".exe;.msi", false);
4343

44-
Assert.True(TestCommon.RunCommand("certutil.exe", "-addstore -f \"TRUSTEDPEOPLE\" " + TestCommon.GetTestDataFile(Constants.AppInstallerTestCert)), "Add AppInstallerTestCert");
45-
4644
if (testParams.PackagedContext)
4745
{
4846
if (testParams.LooseFileRegistration)
@@ -83,8 +81,6 @@ public void TearDown()
8381
this.DecreaseFileTypeRisk(defaultFileTypes, true);
8482
}
8583

86-
TestCommon.RunCommand("certutil.exe", $"-delstore \"TRUSTEDPEOPLE\" {Constants.AppInstallerTestCertThumbprint}");
87-
8884
TestCommon.PublishE2ETestLogs();
8985

9086
if (TestSetup.Parameters.PackagedContext)
Binary file not shown.
Binary file not shown.

src/AppInstallerCLIE2ETests/TestData/localsource.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
}
4545
],
4646
"Signature": {
47-
"CertFile": "%APPINSTALLERTEST_SECUREFILEPATH%"
47+
"CertFile": "%TestSigningCert_PfxPath%",
48+
"Password": "%TestSigningCert_Password%"
4849
}
4950
}

src/LocalhostWebServer/InstallDevCert.ps1

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/LocalhostWebServer/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private static void CopyDirectoryRecursive(string sourceDir, string destDir)
164164
foreach (string file in files)
165165
{
166166
string dest = Path.Combine(destDir, Path.GetFileName(file));
167-
File.Copy(file, dest);
167+
File.Copy(file, dest, overwrite: true);
168168
}
169169

170170
string[] directories = Directory.GetDirectories(sourceDir);

src/LocalhostWebServer/Run-LocalhostWebServer.ps1

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,19 @@ if (-not [System.String]::IsNullOrEmpty($sourceCert))
5454

5555
Push-Location $BuildRoot
5656

57-
$Local:process = Start-Process -FilePath "LocalhostWebServer.exe" -ArgumentList "StaticFileRoot=$StaticFileRoot CertPath=$CertPath CertPassword=$CertPassword OutCertFile=$OutCertFile LocalSourceJson=$LocalSourceJson TestDataPath=$TestDataPath ExitBeforeRun=$ExitBeforeRun" -PassThru
57+
$startProcessArguments = @{
58+
FilePath = Join-Path $BuildRoot "LocalhostWebServer.exe"
59+
ArgumentList = "StaticFileRoot=$StaticFileRoot CertPath=$CertPath CertPassword=$CertPassword OutCertFile=$OutCertFile LocalSourceJson=$LocalSourceJson TestDataPath=$TestDataPath ExitBeforeRun=$ExitBeforeRun"
60+
PassThru = $true
61+
}
62+
63+
if (-not [System.string]::IsNullOrEmpty($env:artifactsDir))
64+
{
65+
$startProcessArguments.RedirectStandardOutput = Join-Path $env:artifactsDir "LocalhostWebServer.out"
66+
$startProcessArguments.RedirectStandardError = Join-Path $env:artifactsDir "LocalhostWebServer.err"
67+
}
5868

69+
$Local:process = Start-Process @startProcessArguments
5970

6071
if ($ExitBeforeRun)
6172
{

templates/e2e-setup.yml

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,48 @@ parameters:
44
type: string
55
- name: localhostWebServerArgs
66
type: string
7+
- name: signingCertOutDir
8+
type: string
9+
default: $(Agent.TempDirectory)
710

811
steps:
9-
- task: DownloadSecureFile@1
10-
name: AppInstallerTest
11-
displayName: 'Download Source Package Certificate'
12-
inputs:
13-
secureFile: 'AppInstallerTest.pfx'
12+
- pwsh: |
13+
$newCertArguments = @{
14+
Type = "Custom"
15+
Subject = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
16+
KeyUsage = "DigitalSignature"
17+
TextExtension = @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")
18+
CertStoreLocation = "Cert:\CurrentUser\My"
19+
}
20+
$cert = New-SelfSignedCertificate @newCertArguments
1421
15-
- task: DownloadSecureFile@1
16-
name: HTTPSDevCert
17-
displayName: 'Download Kestrel Certificate'
18-
inputs:
19-
secureFile: 'HTTPSDevCertV2.pfx'
22+
$certPfxPath = Join-Path $(Agent.TempDirectory) TestSigningCert.pfx
23+
$certCerPath = Join-Path ${{ parameters.signingCertOutDir }} TestSigningCert.cer
24+
$certPassword = (New-Guid).ToString()
25+
$certSecurePassword = ConvertTo-SecureString $certPassword -AsPlainText
2026
21-
- task: PowerShell@2
22-
displayName: Install Root Certificate
23-
inputs:
24-
filePath: '${{ parameters.sourceDir }}\src\LocalhostWebServer\InstallDevCert.ps1'
25-
arguments: '-pfxpath $(HTTPSDevCert.secureFilePath) -password microsoft'
27+
Export-PfxCertificate -Cert $cert -FilePath $certPfxPath -Password $certSecurePassword
28+
Export-Certificate -Cert $cert -FilePath $certCerPath
2629
27-
- task: PowerShell@2
30+
Write-Host "##vso[task.setvariable variable=TestSigningCert.PfxPath;]$certPfxPath"
31+
Write-Host "##vso[task.setvariable variable=TestSigningCert.CerPath;]$certCerPath"
32+
Write-Host "##vso[task.setvariable variable=TestSigningCert.Password;]$certPassword"
33+
displayName: Create test codesigning cert
34+
35+
- pwsh: |
36+
$httpsCertPath = Join-Path $(Agent.TempDirectory) HttpsCert.pfx
37+
$httpsCertPassword = (New-Guid).ToString()
38+
dotnet dev-certs https --export-path $httpsCertPath --password $httpsCertPassword
39+
40+
$securePassword = ConvertTo-SecureString $httpsCertPassword -AsPlainText
41+
Import-PfxCertificate -FilePath $httpsCertPath -Password $securePassword -CertStoreLocation Cert:\LocalMachine\Root
42+
43+
Write-Host "##vso[task.setvariable variable=HttpsCert.Path;]$httpsCertPath"
44+
Write-Host "##vso[task.setvariable variable=HttpsCert.Password;]$httpsCertPassword"
45+
displayName: Create and install localhost HTTPS cert
46+
47+
- pwsh: ${{ parameters.sourceDir }}\src\LocalhostWebServer\Run-LocalhostWebServer.ps1 -CertPath $(HttpsCert.Path) -CertPassword $(HttpsCert.Password) -OutCertFile $(Agent.TempDirectory)\servercert.cer ${{ parameters.localhostWebServerArgs }}
2848
displayName: Launch LocalhostWebServer
29-
inputs:
30-
filePath: '${{ parameters.sourceDir }}\src\LocalhostWebServer\Run-LocalhostWebServer.ps1'
31-
arguments: '-CertPath $(HTTPSDevCert.secureFilePath) -CertPassword microsoft -OutCertFile $(Agent.TempDirectory)\servercert.cer ${{ parameters.localhostWebServerArgs }}'
3249

3350
- task: PowerShell@2
3451
displayName: Setup Local PS Repository

0 commit comments

Comments
 (0)