@@ -0,0 +1,54 @@
|
||||
param([Parameter(Mandatory=$true)][string]$Archive)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$archivePath = (Resolve-Path -LiteralPath $Archive).Path
|
||||
$archiveDirectory = Split-Path -Parent $archivePath
|
||||
$sidecarPath = $archivePath + '.sha256'
|
||||
if (-not (Test-Path -LiteralPath $sidecarPath -PathType Leaf)) { throw 'Archive checksum sidecar is missing' }
|
||||
$sidecarHash = ((Get-Content -LiteralPath $sidecarPath -Raw).Trim() -split '\s+')[0]
|
||||
$archiveHash = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
if ($sidecarHash -ne $archiveHash) { throw 'Archive checksum sidecar does not match' }
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
$zip = [IO.Compression.ZipFile]::OpenRead($archivePath)
|
||||
try {
|
||||
$forbidden = @($zip.Entries | Where-Object {
|
||||
$_.FullName -match '(^|/)(\.env|node_modules|runtime)(/|$)' -or
|
||||
$_.FullName -match '\.(pem|key|p12|pfx)$'
|
||||
})
|
||||
if ($forbidden.Count -gt 0) { throw ('Bundle contains forbidden entries: ' + ($forbidden.FullName -join ', ')) }
|
||||
$entryCount = $zip.Entries.Count
|
||||
} finally {
|
||||
$zip.Dispose()
|
||||
}
|
||||
|
||||
$verificationRoot = Join-Path $archiveDirectory ('.verify-' + [Guid]::NewGuid().ToString('N'))
|
||||
New-Item -ItemType Directory -Path $verificationRoot | Out-Null
|
||||
try {
|
||||
Expand-Archive -LiteralPath $archivePath -DestinationPath $verificationRoot
|
||||
$payload = @(Get-ChildItem -LiteralPath $verificationRoot -Directory)
|
||||
if ($payload.Count -ne 1) { throw 'Bundle must contain exactly one top-level directory' }
|
||||
$manifestPath = Join-Path $payload[0].FullName 'deploy-manifest.json'
|
||||
$checksumsPath = Join-Path $payload[0].FullName 'SHA256SUMS'
|
||||
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
|
||||
if ($manifest.schema_version -ne 'server-deployment-bundle-v1' -or $manifest.schema_migration_target -ne 52 -or $manifest.hardware_runtime -ne 'volc_hardware_agent') {
|
||||
throw 'Deployment manifest metadata is invalid'
|
||||
}
|
||||
$verifiedFiles = 0
|
||||
foreach ($line in Get-Content -LiteralPath $checksumsPath) {
|
||||
if ($line -notmatch '^([0-9a-f]{64}) (.+)$') { throw "Invalid checksum line: $line" }
|
||||
$target = [IO.Path]::GetFullPath((Join-Path $payload[0].FullName $Matches[2]))
|
||||
if (-not $target.StartsWith($payload[0].FullName + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { throw 'Checksum target escapes bundle root' }
|
||||
if (-not (Test-Path -LiteralPath $target -PathType Leaf)) { throw "Checksum target is missing: $($Matches[2])" }
|
||||
if ((Get-FileHash -LiteralPath $target -Algorithm SHA256).Hash.ToLowerInvariant() -ne $Matches[1]) { throw "Checksum mismatch: $($Matches[2])" }
|
||||
$verifiedFiles++
|
||||
}
|
||||
if ($verifiedFiles -ne ($manifest.files.Count + 1)) { throw 'Checksum coverage does not match manifest plus manifest file' }
|
||||
[ordered]@{ archive = $archivePath; sha256 = $archiveHash; zip_entries = $entryCount; verified_files = $verifiedFiles; candidate_version = $manifest.candidate_version; schema_migration_target = $manifest.schema_migration_target } | ConvertTo-Json -Compress
|
||||
} finally {
|
||||
$resolvedVerificationRoot = [IO.Path]::GetFullPath($verificationRoot)
|
||||
$resolvedArchiveDirectory = [IO.Path]::GetFullPath($archiveDirectory)
|
||||
if ($resolvedVerificationRoot.StartsWith($resolvedArchiveDirectory + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase) -and (Test-Path -LiteralPath $resolvedVerificationRoot)) {
|
||||
Remove-Item -LiteralPath $resolvedVerificationRoot -Recurse -Force
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user