1+ # !/usr/bin/env pwsh
2+ <#
3+ . SYNOPSIS
4+ Validates a generated IntegrityTree.json against the files in a directory.
5+
6+ . DESCRIPTION
7+ This mirrors UniGetUI runtime integrity verification and can optionally fail
8+ if the directory contains files that are not listed in IntegrityTree.json.
9+
10+ . PARAMETER Path
11+ The directory containing IntegrityTree.json and the files to validate.
12+
13+ . PARAMETER FailOnUnexpectedFiles
14+ Fail validation if files exist in the directory tree but are not present in
15+ IntegrityTree.json.
16+ #>
17+
18+ [CmdletBinding ()]
19+ param (
20+ [Parameter (Mandatory , Position = 0 )]
21+ [string ] $Path ,
22+
23+ [switch ] $FailOnUnexpectedFiles
24+ )
25+
26+ $ErrorActionPreference = ' Stop'
27+
28+ if (-not (Test-Path $Path - PathType Container)) {
29+ throw " The directory '$Path ' does not exist."
30+ }
31+
32+ $Path = (Resolve-Path $Path ).Path
33+ $IntegrityTreePath = Join-Path $Path ' IntegrityTree.json'
34+
35+ if (-not (Test-Path $IntegrityTreePath - PathType Leaf)) {
36+ throw " IntegrityTree.json was not found in '$Path '."
37+ }
38+
39+ $rawData = Get-Content $IntegrityTreePath - Raw
40+
41+ try {
42+ $data = ConvertFrom-Json $rawData - AsHashtable
43+ }
44+ catch {
45+ throw " IntegrityTree.json is not valid JSON: $ ( $_.Exception.Message ) "
46+ }
47+
48+ if ($null -eq $data ) {
49+ throw ' IntegrityTree.json did not deserialize into a JSON object.'
50+ }
51+
52+ $missingFiles = New-Object System.Collections.Generic.List[string ]
53+ $mismatchedFiles = New-Object System.Collections.Generic.List[string ]
54+ $unexpectedFiles = New-Object System.Collections.Generic.List[string ]
55+
56+ $expectedFiles = @ {}
57+ foreach ($entry in $data.GetEnumerator ()) {
58+ $relativePath = [string ] $entry.Key
59+ $expectedHash = [string ] $entry.Value
60+ $expectedFiles [$relativePath ] = $true
61+
62+ $fullPath = Join-Path $Path $relativePath
63+ if (-not (Test-Path $fullPath - PathType Leaf)) {
64+ $missingFiles.Add ($relativePath )
65+ continue
66+ }
67+
68+ $currentHash = (Get-FileHash $fullPath - Algorithm SHA256).Hash.ToLowerInvariant()
69+ if ($currentHash -ne $expectedHash.ToLowerInvariant ()) {
70+ $mismatchedFiles.Add (" $relativePath |expected=$expectedHash |got=$currentHash " )
71+ }
72+ }
73+
74+ if ($FailOnUnexpectedFiles ) {
75+ Get-ChildItem $Path - Recurse - File | ForEach-Object {
76+ $relativePath = $_.FullName.Substring ($Path.Length ).TrimStart(' \' , ' /' ) -replace ' \\' , ' /'
77+ if ($relativePath -eq ' IntegrityTree.json' ) {
78+ return
79+ }
80+
81+ if (-not $expectedFiles.ContainsKey ($relativePath )) {
82+ $unexpectedFiles.Add ($relativePath )
83+ }
84+ }
85+ }
86+
87+ if ($missingFiles.Count -or $mismatchedFiles.Count -or $unexpectedFiles.Count ) {
88+ if ($missingFiles.Count ) {
89+ Write-Error " Missing files listed in IntegrityTree.json:`n - $ ( $missingFiles -join " `n - " ) "
90+ }
91+
92+ if ($mismatchedFiles.Count ) {
93+ Write-Error " Files with mismatched SHA256 values:`n - $ ( $mismatchedFiles -join " `n - " ) "
94+ }
95+
96+ if ($unexpectedFiles.Count ) {
97+ Write-Error " Unexpected files not present in IntegrityTree.json:`n - $ ( $unexpectedFiles -join " `n - " ) "
98+ }
99+
100+ throw ' Integrity tree validation failed.'
101+ }
102+
103+ $validatedFileCount = $data.Count
104+ Write-Host " Integrity tree validation succeeded for $validatedFileCount file(s) in $Path "
0 commit comments