Files
ARCADIA-Studio/harness/experiments/navigation-prerelease-native-20260911/payload/unbound-draft/loaded-modules.ps1
T

45 lines
2.8 KiB
PowerShell

# Optional read-only process inventory. Never launches, closes or controls a GUI.
#Requires -Version 7.0
$ErrorActionPreference = 'Stop'
$trialPath = Join-Path $PSScriptRoot 'trial'
$resultPath = Join-Path $trialPath 'loaded-modules.json'
if (Test-Path -LiteralPath $resultPath) { throw 'Preserve existing module receipt.' }
$launch = Get-Content -LiteralPath (Join-Path $trialPath 'launch.json') -Raw | ConvertFrom-Json
$identity = Get-Content -LiteralPath (Join-Path $trialPath 'process-identity.json') -Raw | ConvertFrom-Json
$before = Get-Content -LiteralPath (Join-Path $trialPath 'package-before.json') -Raw | ConvertFrom-Json
$receipt = [ordered]@{ atUtc = [DateTime]::UtcNow.ToString('o'); pid = $launch.pid; status = 'PENDING'; modules = @(); scope = 'Point-in-time loaded modules of the exact launched process; no GUI action or universal runtime dependency claim.' }
try {
$guiProcess = Get-Process -Id $launch.pid -ErrorAction Stop
if (-not [string]::Equals($guiProcess.Path, $identity.path, [StringComparison]::OrdinalIgnoreCase) -or
$guiProcess.StartTime.ToUniversalTime().ToFileTimeUtc() -ne $identity.creation_filetime) {
throw 'PID no longer identifies the original launch.'
}
$receipt.creation_filetime = $identity.creation_filetime
$systemRoot = [IO.Path]::GetFullPath($launch.environment.SystemRoot).TrimEnd('\') + '\'
$packageRoot = [IO.Path]::GetFullPath($launch.package).TrimEnd('\') + '\'
$items = foreach ($module in $guiProcess.Modules) {
$full = [IO.Path]::GetFullPath($module.FileName)
$inWindows = $full.StartsWith($systemRoot, [StringComparison]::OrdinalIgnoreCase)
$inPackage = $full.StartsWith($packageRoot, [StringComparison]::OrdinalIgnoreCase)
$hash = (Get-FileHash -LiteralPath $full -Algorithm SHA256).Hash.ToLowerInvariant()
if ($inPackage) {
$relative = $full.Substring($packageRoot.Length).Replace('\', '/')
$recorded = $before.PSObject.Properties[$relative]
if (-not $recorded -or $recorded.Value -cne $hash) { throw "Package module differs: $relative" }
}
[ordered]@{ name = $module.ModuleName; path = $full; sha256 = $hash; windowsPath = $inWindows; packagePath = $inPackage }
}
$receipt.modules = @($items)
if (-not $receipt.modules.Count) { throw 'No modules observed.' }
$unexpected = @($items | Where-Object { -not $_.windowsPath -and -not $_.packagePath })
$receipt.unexpected = $unexpected
if ($unexpected.Count) { throw 'Unexpected non-Windows, non-package module; inspect receipt.' }
$receipt.status = 'PASS'
} catch {
$receipt.status = 'FAIL'
$receipt.error = $_.ToString()
} finally {
[IO.File]::WriteAllText($resultPath, ($receipt | ConvertTo-Json -Depth 10) + "`n", [Text.UTF8Encoding]::new($false))
}
if ($receipt.status -ne 'PASS') { throw $receipt.error }