microsoft/hve-core

Public

mirrored from https://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/a11y-pr6-docs

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/tests/security/Sign-PlannerArtifacts.Tests.ps1

378lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4
5BeforeAll {
6 # Stub cosign when not installed so Pester can mock it
7 if (-not (Get-Command cosign -ErrorAction SilentlyContinue)) { function global:cosign { } }
8
9 $script:ScriptPath = Join-Path $PSScriptRoot '../../security/Sign-PlannerArtifacts.ps1'
10
11 # Extract helper functions via AST. The script has a mandatory ProjectSlug
12 # parameter with script-scope execution, preventing dot-source.
13 $ast = [System.Management.Automation.Language.Parser]::ParseFile(
14 $script:ScriptPath, [ref]$null, [ref]$null)
15 $ast.FindAll(
16 { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true
17 ) | ForEach-Object { . ([scriptblock]::Create($_.Extent.Text)) }
18
19 Mock Write-Host {}
20}
21
22Describe 'Get-ArtifactHash' -Tag 'Unit' {
23 Context 'SHA-256 computation' {
24 It 'Returns a lowercase hex SHA-256 hash for a file' {
25 $testFile = Join-Path $TestDrive 'hash-test.txt'
26 Set-Content -Path $testFile -Value 'deterministic content' -NoNewline -Encoding utf8NoBOM
27
28 $hash = Get-ArtifactHash -FilePath $testFile
29
30 $hash | Should -Match '^[0-9a-f]{64}$'
31 }
32
33 It 'Returns consistent hashes for identical content' {
34 $file1 = Join-Path $TestDrive 'dup1.txt'
35 $file2 = Join-Path $TestDrive 'dup2.txt'
36 Set-Content -Path $file1 -Value 'same' -NoNewline -Encoding utf8NoBOM
37 Set-Content -Path $file2 -Value 'same' -NoNewline -Encoding utf8NoBOM
38
39 $hash1 = Get-ArtifactHash -FilePath $file1
40 $hash2 = Get-ArtifactHash -FilePath $file2
41
42 $hash1 | Should -Be $hash2
43 }
44
45 It 'Returns different hashes for different content' {
46 $file1 = Join-Path $TestDrive 'diff1.txt'
47 $file2 = Join-Path $TestDrive 'diff2.txt'
48 Set-Content -Path $file1 -Value 'alpha' -NoNewline -Encoding utf8NoBOM
49 Set-Content -Path $file2 -Value 'bravo' -NoNewline -Encoding utf8NoBOM
50
51 $hash1 = Get-ArtifactHash -FilePath $file1
52 $hash2 = Get-ArtifactHash -FilePath $file2
53
54 $hash1 | Should -Not -Be $hash2
55 }
56 }
57}
58
59Describe 'Manifest Generation' -Tag 'Unit' {
60 BeforeEach {
61 $script:projectSlug = 'test-project'
62 $script:artifactDir = Join-Path $TestDrive ".copilot-tracking/rai-plans/$($script:projectSlug)"
63 if (Test-Path $script:artifactDir) { Remove-Item $script:artifactDir -Recurse -Force }
64 New-Item -ItemType Directory -Path $script:artifactDir -Force | Out-Null
65 }
66
67 Context 'when artifact directory does not exist' {
68 It 'Exits with code 1 for missing directory' {
69 $missingSlug = 'nonexistent-project'
70 $originalPWD = $PWD
71 try {
72 Set-Location $TestDrive
73 & $script:ScriptPath -ProjectSlug $missingSlug
74 }
75 catch { $_ | Out-Null }
76 finally {
77 Set-Location $originalPWD
78 }
79 $LASTEXITCODE | Should -Be 1
80 }
81 }
82
83 Context 'when artifact directory is empty' {
84 It 'Exits with code 0 and reports no artifacts' {
85 $originalPWD = $PWD
86 try {
87 Set-Location $TestDrive
88 & $script:ScriptPath -ProjectSlug $script:projectSlug
89 }
90 catch { $_ | Out-Null }
91 finally {
92 Set-Location $originalPWD
93 }
94
95 Should -Invoke Write-Host -ParameterFilter { $Object -like '*No artifacts found*' }
96 }
97 }
98
99 Context 'when artifacts exist' {
100 BeforeEach {
101 Set-Content -Path (Join-Path $script:artifactDir 'state.json') -Value '{"status":"complete"}' -Encoding utf8NoBOM
102 Set-Content -Path (Join-Path $script:artifactDir 'findings.md') -Value '# Findings' -Encoding utf8NoBOM
103 $script:outputPath = Join-Path $script:artifactDir 'artifact-manifest.json'
104 }
105
106 It 'Generates a valid JSON manifest' {
107 $originalPWD = $PWD
108 try {
109 Set-Location $TestDrive
110 & $script:ScriptPath -ProjectSlug $script:projectSlug
111 }
112 finally {
113 Set-Location $originalPWD
114 }
115
116 Test-Path $script:outputPath | Should -BeTrue
117 $manifest = Get-Content $script:outputPath -Raw | ConvertFrom-Json
118 $manifest.version | Should -Be '1.0'
119 $manifest.projectSlug | Should -Be $script:projectSlug
120 $manifest.algorithm | Should -Be 'SHA256'
121 }
122
123 It 'Includes correct file count' {
124 $originalPWD = $PWD
125 try {
126 Set-Location $TestDrive
127 & $script:ScriptPath -ProjectSlug $script:projectSlug
128 }
129 finally {
130 Set-Location $originalPWD
131 }
132
133 $manifest = Get-Content $script:outputPath -Raw | ConvertFrom-Json
134 $manifest.fileCount | Should -Be 2
135 $manifest.artifacts.Count | Should -Be 2
136 }
137
138 It 'Computes SHA-256 hashes for each artifact' {
139 $originalPWD = $PWD
140 try {
141 Set-Location $TestDrive
142 & $script:ScriptPath -ProjectSlug $script:projectSlug
143 }
144 finally {
145 Set-Location $originalPWD
146 }
147
148 $manifest = Get-Content $script:outputPath -Raw | ConvertFrom-Json
149 foreach ($artifact in $manifest.artifacts) {
150 $artifact.sha256 | Should -Match '^[0-9a-f]{64}$'
151 $artifact.path | Should -Not -BeNullOrEmpty
152 $artifact.sizeBytes | Should -BeGreaterThan 0
153 }
154 }
155
156 It 'Writes manifest to custom OutputPath when specified' {
157 $customPath = Join-Path $TestDrive 'custom-manifest.json'
158
159 $originalPWD = $PWD
160 try {
161 Set-Location $TestDrive
162 & $script:ScriptPath -ProjectSlug $script:projectSlug -OutputPath $customPath
163 }
164 finally {
165 Set-Location $originalPWD
166 }
167
168 Test-Path $customPath | Should -BeTrue
169 $manifest = Get-Content $customPath -Raw | ConvertFrom-Json
170 $manifest.fileCount | Should -Be 2
171 }
172
173 It 'Includes generatedAt in ISO 8601 format' {
174 $originalPWD = $PWD
175 try {
176 Set-Location $TestDrive
177 & $script:ScriptPath -ProjectSlug $script:projectSlug
178 }
179 finally {
180 Set-Location $originalPWD
181 }
182
183 $raw = Get-Content $script:outputPath -Raw
184 $raw | Should -Match '"generatedAt"\s*:\s*"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}'
185 }
186
187 It 'Orders artifacts alphabetically by path' {
188 $originalPWD = $PWD
189 try {
190 Set-Location $TestDrive
191 & $script:ScriptPath -ProjectSlug $script:projectSlug
192 }
193 finally {
194 Set-Location $originalPWD
195 }
196
197 $manifest = Get-Content $script:outputPath -Raw | ConvertFrom-Json
198 $paths = $manifest.artifacts | ForEach-Object { $_.path }
199 $sorted = $paths | Sort-Object
200 $paths | Should -Be $sorted
201 }
202
203 It 'Contains all required top-level manifest fields' {
204 $originalPWD = $PWD
205 try {
206 Set-Location $TestDrive
207 & $script:ScriptPath -ProjectSlug $script:projectSlug
208 }
209 finally {
210 Set-Location $originalPWD
211 }
212
213 $manifest = Get-Content $script:outputPath -Raw | ConvertFrom-Json
214 $fields = ($manifest | Get-Member -MemberType NoteProperty).Name | Sort-Object
215 $expected = @('algorithm', 'artifacts', 'fileCount', 'generatedAt', 'projectSlug', 'sessionPath', 'version') | Sort-Object
216 $fields | Should -Be $expected
217 }
218 }
219
220 Context 'when SessionPath is specified' {
221 It 'Generates a manifest for a repo-relative session path' {
222 $relativeSessionPath = '.copilot-tracking/sssc-plans/test-sssc-session'
223 $sessionDir = Join-Path $TestDrive $relativeSessionPath
224 New-Item -ItemType Directory -Path $sessionDir -Force | Out-Null
225 Set-Content -Path (Join-Path $sessionDir 'state.json') -Value '{}' -Encoding utf8NoBOM
226
227 $originalPWD = $PWD
228 try {
229 Set-Location $TestDrive
230 & $script:ScriptPath -SessionPath $relativeSessionPath
231 }
232 finally {
233 Set-Location $originalPWD
234 }
235
236 $outputPath = Join-Path $sessionDir 'artifact-manifest.json'
237 Test-Path $outputPath | Should -BeTrue
238 $manifest = Get-Content $outputPath -Raw | ConvertFrom-Json
239 $manifest.projectSlug | Should -Be 'test-sssc-session'
240 $manifest.sessionPath | Should -Be '.copilot-tracking/sssc-plans/test-sssc-session'
241 $manifest.fileCount | Should -Be 1
242 }
243
244 It 'Generates a manifest for an absolute session path' {
245 $sessionDir = Join-Path $TestDrive '.copilot-tracking/sssc-plans/absolute-session'
246 New-Item -ItemType Directory -Path $sessionDir -Force | Out-Null
247 Set-Content -Path (Join-Path $sessionDir 'state.json') -Value '{}' -Encoding utf8NoBOM
248
249 $originalPWD = $PWD
250 try {
251 Set-Location (Split-Path -Path $script:ScriptPath -Parent)
252 & $script:ScriptPath -SessionPath $sessionDir
253 }
254 finally {
255 Set-Location $originalPWD
256 }
257
258 $outputPath = Join-Path $sessionDir 'artifact-manifest.json'
259 Test-Path $outputPath | Should -BeTrue
260 $manifest = Get-Content $outputPath -Raw | ConvertFrom-Json
261 $manifest.projectSlug | Should -Be 'absolute-session'
262 $manifest.sessionPath | Should -Be ($sessionDir -replace '\\','/')
263 $manifest.fileCount | Should -Be 1
264 }
265
266 It 'Uses a custom manifest name and excludes it from artifact inventory' {
267 $relativeSessionPath = '.copilot-tracking/sssc-plans/custom-manifest-session'
268 $sessionDir = Join-Path $TestDrive $relativeSessionPath
269 New-Item -ItemType Directory -Path $sessionDir -Force | Out-Null
270 Set-Content -Path (Join-Path $sessionDir 'state.json') -Value '{}' -Encoding utf8NoBOM
271 Set-Content -Path (Join-Path $sessionDir 'sssc-manifest.json') -Value '{}' -Encoding utf8NoBOM
272
273 $originalPWD = $PWD
274 try {
275 Set-Location $TestDrive
276 & $script:ScriptPath -SessionPath $relativeSessionPath -ManifestName 'sssc-manifest.json'
277 }
278 finally {
279 Set-Location $originalPWD
280 }
281
282 $outputPath = Join-Path $sessionDir 'sssc-manifest.json'
283 Test-Path $outputPath | Should -BeTrue
284 $manifest = Get-Content $outputPath -Raw | ConvertFrom-Json
285 $manifest.fileCount | Should -Be 1
286 $manifest.artifacts[0].path | Should -Be 'state.json'
287 }
288 }
289
290 Context 'exclude patterns' {
291 It 'Excludes artifact-manifest.json from the inventory' {
292 Set-Content -Path (Join-Path $script:artifactDir 'state.json') -Value '{}' -Encoding utf8NoBOM
293 Set-Content -Path (Join-Path $script:artifactDir 'artifact-manifest.json') -Value '{}' -Encoding utf8NoBOM
294
295 $outputPath = Join-Path $script:artifactDir 'artifact-manifest.json'
296 $originalPWD = $PWD
297 try {
298 Set-Location $TestDrive
299 & $script:ScriptPath -ProjectSlug $script:projectSlug
300 }
301 finally {
302 Set-Location $originalPWD
303 }
304
305 $manifest = Get-Content $outputPath -Raw | ConvertFrom-Json
306 $manifest.fileCount | Should -Be 1
307 $manifest.artifacts[0].path | Should -Be 'state.json'
308 }
309
310 It 'Excludes .sig and .bundle files from the inventory' {
311 Set-Content -Path (Join-Path $script:artifactDir 'data.md') -Value '# Data' -Encoding utf8NoBOM
312 Set-Content -Path (Join-Path $script:artifactDir 'manifest.json.sig') -Value 'sig' -Encoding utf8NoBOM
313 Set-Content -Path (Join-Path $script:artifactDir 'manifest.json.bundle') -Value 'bundle' -Encoding utf8NoBOM
314
315 $originalPWD = $PWD
316 try {
317 Set-Location $TestDrive
318 & $script:ScriptPath -ProjectSlug $script:projectSlug
319 }
320 finally {
321 Set-Location $originalPWD
322 }
323
324 $manifest = Get-Content $script:outputPath -Raw | ConvertFrom-Json
325 $manifest.fileCount | Should -Be 1
326 $manifest.artifacts[0].path | Should -Be 'data.md'
327 }
328 }
329}
330
331Describe 'Cosign Signing' -Tag 'Unit' {
332 BeforeEach {
333 $script:projectSlug = 'cosign-test'
334 $script:artifactDir = Join-Path $TestDrive ".copilot-tracking/rai-plans/$($script:projectSlug)"
335 if (Test-Path $script:artifactDir) { Remove-Item $script:artifactDir -Recurse -Force }
336 New-Item -ItemType Directory -Path $script:artifactDir -Force | Out-Null
337 Set-Content -Path (Join-Path $script:artifactDir 'data.md') -Value '# Test' -Encoding utf8NoBOM
338 $script:outputPath = Join-Path $script:artifactDir 'artifact-manifest.json'
339 }
340
341 Context 'when cosign is not installed' {
342 It 'Warns and skips signing gracefully' {
343 Mock Get-Command { $null } -ParameterFilter { $Name -eq 'cosign' }
344 Mock cosign {}
345
346 $originalPWD = $PWD
347 try {
348 Set-Location $TestDrive
349 & $script:ScriptPath -ProjectSlug $script:projectSlug -IncludeCosign
350 }
351 catch { $_ | Out-Null }
352 finally {
353 Set-Location $originalPWD
354 }
355
356 Should -Invoke Write-Host -ParameterFilter { $Object -like '*cosign not found*' }
357 Should -Not -Invoke cosign
358 }
359 }
360
361 Context 'when cosign is available' {
362 It 'Invokes cosign sign-blob with correct arguments' {
363 Mock Get-Command { [pscustomobject]@{ Name = 'cosign' } } -ParameterFilter { $Name -eq 'cosign' }
364 Mock cosign {}
365
366 $originalPWD = $PWD
367 try {
368 Set-Location $TestDrive
369 & $script:ScriptPath -ProjectSlug $script:projectSlug -IncludeCosign
370 }
371 finally {
372 Set-Location $originalPWD
373 }
374
375 Should -Invoke cosign -Times 1 -Exactly
376 }
377 }
378}
379