fix(launcher): harden LA11 gate evidence
This commit is contained in:
parent
accd01a008
commit
9f9c116792
7 changed files with 514 additions and 89 deletions
|
|
@ -165,6 +165,84 @@ function Write-PayloadFile([string]$Root, [string]$Name, [string]$Content) {
|
|||
$null = New-Item -ItemType Directory -Force -Path $directory
|
||||
[IO.File]::WriteAllText($path, $Content, [Text.UTF8Encoding]::new($false))
|
||||
}
|
||||
|
||||
function Get-ZipUInt16([byte[]]$Bytes, [int]$Offset) {
|
||||
return [int]$Bytes[$Offset] -bor ([int]$Bytes[$Offset + 1] -shl 8)
|
||||
}
|
||||
function Get-ZipUInt32([byte[]]$Bytes, [int]$Offset) {
|
||||
return [uint32]([uint32]$Bytes[$Offset] -bor
|
||||
([uint32]$Bytes[$Offset + 1] -shl 8) -bor
|
||||
([uint32]$Bytes[$Offset + 2] -shl 16) -bor
|
||||
([uint32]$Bytes[$Offset + 3] -shl 24))
|
||||
}
|
||||
function Test-ZipExecutableName([string]$Name) {
|
||||
return $Name -cin @(
|
||||
'AcDream.App', 'acdream-headless', 'acdream-launcher', 'acdream-bake')
|
||||
}
|
||||
function Assert-ZipUnixMetadata([string]$Path) {
|
||||
[byte[]]$bytes = [IO.File]::ReadAllBytes($Path)
|
||||
$eocd = $bytes.Length - 22
|
||||
if ($eocd -lt 0 -or (Get-ZipUInt32 $bytes $eocd) -ne 0x06054b50 -or
|
||||
(Get-ZipUInt16 $bytes ($eocd + 20)) -ne 0) {
|
||||
throw "Fixture ZIP end record is invalid: $Path"
|
||||
}
|
||||
$entryCount = Get-ZipUInt16 $bytes ($eocd + 10)
|
||||
$centralSize = Get-ZipUInt32 $bytes ($eocd + 12)
|
||||
[uint64]$cursor = Get-ZipUInt32 $bytes ($eocd + 16)
|
||||
$centralEnd = $cursor + $centralSize
|
||||
if ($centralEnd -ne $eocd) { throw "Fixture ZIP central bounds are invalid: $Path" }
|
||||
$rawModes = @{}
|
||||
for ($index = 0; $index -lt $entryCount; $index++) {
|
||||
if ($cursor + 46 -gt $centralEnd -or
|
||||
(Get-ZipUInt32 $bytes ([int]$cursor)) -ne 0x02014b50) {
|
||||
throw "Fixture ZIP central entry is invalid: $Path"
|
||||
}
|
||||
if ($bytes[[int]$cursor + 5] -ne 3) {
|
||||
throw "Fixture ZIP entry origin is not Unix: $Path"
|
||||
}
|
||||
$nameLength = Get-ZipUInt16 $bytes ([int]$cursor + 28)
|
||||
$extraLength = Get-ZipUInt16 $bytes ([int]$cursor + 30)
|
||||
$commentLength = Get-ZipUInt16 $bytes ([int]$cursor + 32)
|
||||
$name = [Text.Encoding]::UTF8.GetString(
|
||||
$bytes,
|
||||
[int]$cursor + 46,
|
||||
$nameLength)
|
||||
$expectedMode = if (Test-ZipExecutableName $name) { 0x81ED } else { 0x81A4 }
|
||||
$external = Get-ZipUInt32 $bytes ([int]$cursor + 38)
|
||||
$expectedExternal = [uint32](([uint64]$expectedMode) -shl 16)
|
||||
if ($external -ne $expectedExternal) {
|
||||
throw "Fixture ZIP entry '$name' has wrong raw type/mode bits."
|
||||
}
|
||||
$rawModes[$name] = $expectedMode
|
||||
$cursor += 46 + $nameLength + $extraLength + $commentLength
|
||||
}
|
||||
if ($cursor -ne $centralEnd) { throw "Fixture ZIP central length is invalid: $Path" }
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
try {
|
||||
$archive = [IO.Compression.ZipArchive]::new(
|
||||
$stream,
|
||||
[IO.Compression.ZipArchiveMode]::Read,
|
||||
$false,
|
||||
[Text.Encoding]::UTF8)
|
||||
try {
|
||||
if ($archive.Entries.Count -ne $rawModes.Count) {
|
||||
throw "Fixture ZIP entry count changed through ZipArchive: $Path"
|
||||
}
|
||||
foreach ($entry in $archive.Entries) {
|
||||
$mode = ($entry.ExternalAttributes -shr 16) -band 0xffff
|
||||
if (-not $rawModes.ContainsKey($entry.FullName) -or
|
||||
$mode -ne $rawModes[$entry.FullName]) {
|
||||
throw "ZipArchive reports wrong type/mode for '$($entry.FullName)'."
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { $archive.Dispose() }
|
||||
}
|
||||
finally { $stream.Dispose() }
|
||||
}
|
||||
|
||||
$payloadRoot = Join-Path $OutputDirectory 'deterministic-payloads'
|
||||
$payloads = [ordered]@{
|
||||
ClientWin = Join-Path $payloadRoot 'client-win'
|
||||
|
|
@ -208,6 +286,9 @@ try {
|
|||
[Globalization.CultureInfo]::CurrentUICulture = $culture
|
||||
$destination = Join-Path $OutputDirectory "fixture-$cultureName"
|
||||
& $fixture -OutputDirectory $destination @fixtureParameters
|
||||
foreach ($zip in @(Get-ChildItem -LiteralPath $destination -Filter '*.zip' -File -Recurse)) {
|
||||
Assert-ZipUnixMetadata $zip.FullName
|
||||
}
|
||||
$relativePaths = [string[]]@(Get-ChildItem -LiteralPath $destination -File -Recurse |
|
||||
Where-Object { $_.Name -ne 'fixture-report.json' } |
|
||||
ForEach-Object {
|
||||
|
|
@ -235,9 +316,49 @@ $digestBytes = [Security.Cryptography.SHA256]::HashData(
|
|||
[Text.Encoding]::UTF8.GetBytes($firstInventory))
|
||||
$deterministicDigest = [Convert]::ToHexString($digestBytes).ToLowerInvariant()
|
||||
$expectedCrossPlatformDigest =
|
||||
'9c77b7204dd19e77fad62e572304d52e810afe2d0821c2ec57a692d27a0cc167'
|
||||
'cc58d5717de6686690b7f01213c9d52a99aef49447ff645e134f8c97ec8e3a76'
|
||||
if ($deterministicDigest -cne $expectedCrossPlatformDigest) {
|
||||
throw 'Fixture artifact hashes differ from the pinned Windows/Linux contract.'
|
||||
throw "Fixture artifact hashes differ from the pinned Windows/Linux contract: actual $deterministicDigest."
|
||||
}
|
||||
|
||||
$nativeExtractionModesValidated = $false
|
||||
if ($IsLinux) {
|
||||
$unzip = @(Get-Command unzip -CommandType Application -ErrorAction Stop)[0].Source
|
||||
$extractClient = Join-Path $OutputDirectory 'native-extract-client'
|
||||
$extractLauncher = Join-Path $OutputDirectory 'native-extract-launcher'
|
||||
$null = New-Item -ItemType Directory -Path $extractClient
|
||||
$null = New-Item -ItemType Directory -Path $extractLauncher
|
||||
& $unzip -qq (Join-Path $OutputDirectory 'fixture-en-US/A/client-linux-x64.zip') `
|
||||
-d $extractClient
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Native client ZIP extraction failed.' }
|
||||
& $unzip -qq (Join-Path $OutputDirectory 'fixture-en-US/A/launcher-linux-x64.zip') `
|
||||
-d $extractLauncher
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Native launcher ZIP extraction failed.' }
|
||||
$mode755 = [IO.UnixFileMode]::UserRead -bor [IO.UnixFileMode]::UserWrite -bor
|
||||
[IO.UnixFileMode]::UserExecute -bor [IO.UnixFileMode]::GroupRead -bor
|
||||
[IO.UnixFileMode]::GroupExecute -bor [IO.UnixFileMode]::OtherRead -bor
|
||||
[IO.UnixFileMode]::OtherExecute
|
||||
$mode644 = [IO.UnixFileMode]::UserRead -bor [IO.UnixFileMode]::UserWrite -bor
|
||||
[IO.UnixFileMode]::GroupRead -bor [IO.UnixFileMode]::OtherRead
|
||||
foreach ($path in @(
|
||||
(Join-Path $extractClient 'AcDream.App'),
|
||||
(Join-Path $extractClient 'acdream-headless'),
|
||||
(Join-Path $extractLauncher 'acdream-launcher'),
|
||||
(Join-Path $extractLauncher 'acdream-bake'))) {
|
||||
if ([IO.File]::GetUnixFileMode($path) -ne $mode755) {
|
||||
throw "Native extraction did not retain mode 0755: $path"
|
||||
}
|
||||
}
|
||||
foreach ($path in @(
|
||||
(Join-Path $extractClient 'nested/I.txt'),
|
||||
(Join-Path $extractClient 'campaign-la-fixture-release.txt'),
|
||||
(Join-Path $extractLauncher 'nested/Z.txt'),
|
||||
(Join-Path $extractLauncher 'campaign-la-fixture-release.txt'))) {
|
||||
if ([IO.File]::GetUnixFileMode($path) -ne $mode644) {
|
||||
throw "Native extraction did not retain mode 0644: $path"
|
||||
}
|
||||
}
|
||||
$nativeExtractionModesValidated = $true
|
||||
}
|
||||
|
||||
$summary = [ordered]@{
|
||||
|
|
@ -248,6 +369,9 @@ $summary = [ordered]@{
|
|||
cultures = @('en-US', 'tr-TR', 'sv-SE')
|
||||
fixtureArtifactSetSha256 = $deterministicDigest
|
||||
crossPlatformExpectedSha256 = $expectedCrossPlatformDigest
|
||||
zipOrigin = 'unix'
|
||||
zipModesValidated = $true
|
||||
nativeExtractionModesValidated = $nativeExtractionModesValidated
|
||||
}
|
||||
$summary | ConvertTo-Json -Depth 5 |
|
||||
Set-Content -LiteralPath (Join-Path $OutputDirectory 'summary.json') -Encoding utf8NoBOM
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue