Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/windows-hardening.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ jobs:
${{ steps.capture_temp.outputs.root }}\gc-ui-capture-*\prebuilt\**\provenance.json
${{ steps.capture_temp.outputs.root }}\gc-ui-capture-*\prebuilt\**\compile*.log
!${{ steps.capture_temp.outputs.root }}\gc-ui-capture-*\suite\**\.git\**
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\*.json
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\*.jsonl
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\*.log
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\prebuilt\fixtures.json
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\prebuilt\**\provenance.json
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\prebuilt\**\compile*.log
!${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\.git\**
if-no-files-found: warn
retention-days: 7

Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/windows-port-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ jobs:
${{ steps.capture_temp.outputs.root }}\gc-ui-capture-*\prebuilt\**\provenance.json
${{ steps.capture_temp.outputs.root }}\gc-ui-capture-*\prebuilt\**\compile*.log
!${{ steps.capture_temp.outputs.root }}\gc-ui-capture-*\suite\**\.git\**
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\*.json
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\*.jsonl
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\*.log
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\prebuilt\fixtures.json
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\prebuilt\**\provenance.json
${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\prebuilt\**\compile*.log
!${{ steps.capture_temp.outputs.root }}\g[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\suite\**\.git\**
if-no-files-found: warn
retention-days: 7

Expand Down
6 changes: 4 additions & 2 deletions Tools/windows/Invoke-UiCapture.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,15 @@ function Assert-CapPreflightReport {
}
}
if ($job.PSObject.Properties['sceneAdapter'] -and $job.sceneAdapter -ceq 'S_MATCHED_V12_V1') {
if ($preflight.executed -ne 178 -or $preflight.skipped -isnot [long] -or $preflight.skipped -ne 0) {
throw 'Matched scene preflight requires exactly 178 passing current contracts and zero skips.'
if ($preflight.executed -ne 180 -or $preflight.skipped -isnot [long] -or $preflight.skipped -ne 0) {
throw 'Matched scene preflight requires exactly 180 passing current contracts and zero skips.'
}
foreach ($name in @(
'matched v12 fixture binds exact node type wiring reason and outcome fields',
'matched v12 fixture refuses identity type wiring selection reason and outcome mutations',
'matched v12 complete scene inventory is admitted and missing scenes refuse',
'matched S tab actual worker seed write preserves scene path and not-created cleanup',
'matched all eighteen scenes across both phases create write and clean isolated seed paths',
'matched tab observations preserve exact owned IDs parents selection and close bounds',
'real restart transcript requires daemon confirmation resume and new owned attach',
'real restart scene invokes existing UIA route and refuses offline peer authority',
Expand Down
90 changes: 83 additions & 7 deletions Tools/windows/Invoke-UiCaptureWorker.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ function Start-CapOfflineSeed {
$arguments[6] -cne 'echo Offline card fixture - no backend')) {
throw 'The B job does not contain the exact seven-argument offline cmd-only zmx seed.'
}
$seedReceiptRoot = if ($isS) { $Roots.Root } else { Join-Path $runDirectory "phases\$Phase\normal" }
$seedReceiptPath = Assert-CapOwnedPath (Join-Path $seedReceiptRoot 'zmx-seed.json')
$null = New-Item -ItemType Directory -Path (Split-Path -Parent $seedReceiptPath) -Force
$zmxRoot = Assert-CapOwnedPath $Roots.Zmx
$zmxWorking = Assert-CapOwnedPath (Join-Path $Roots.Peer 'zmx-cwd')
New-Item -ItemType Directory -Path $zmxWorking -Force | Out-Null
Expand All @@ -442,8 +445,20 @@ function Start-CapOfflineSeed {
$run | Add-Member -NotePropertyName Phase -NotePropertyValue $Phase
$run | Add-Member -NotePropertyName SessionName -NotePropertyValue $arguments[1]
$run | Add-Member -NotePropertyName Descendants -NotePropertyValue @()
$run | Add-Member -NotePropertyName SeedReceiptPath -NotePropertyValue $seedReceiptPath
$run | Add-Member -NotePropertyName SeedCleanupComplete -NotePropertyValue $false
$runs.Add($run)
$seedRuns.Add($run)
$launcherHandle = $run.Identity.Process.Handle
$launcherSnapshot = [CapNative]::Snapshot($launcherHandle,$run.Identity.Pid)
if ($launcherSnapshot.IsExited -or $launcherSnapshot.CreationFileTimeUtc -ne $run.Identity.CreationFileTimeUtc -or
$launcherSnapshot.ImagePath -ine $run.Identity.ExpectedImagePath) {
throw 'Seed launcher original image attestation differs.'
}
$run | Add-Member -NotePropertyName LauncherImageAttestation -NotePropertyValue ([pscustomobject]@{
pid = $launcherSnapshot.ProcessId; creationFileTimeUtc = $launcherSnapshot.CreationFileTimeUtc
imagePath = $launcherSnapshot.ImagePath; heldHandle = $launcherHandle.ToInt64()
})
Write-CapProcessJournal -Identity $run.Identity -Phase $Phase
$target = Release-CapJobLauncher -Run $run -JournalState $journalState
$receipt = [pscustomobject]@{
Expand All @@ -469,12 +484,18 @@ function Start-CapOfflineSeed {
stdoutEof = $false
stderrEof = $false
cleanupStatus = 'PENDING'
receiptPublicationStatus = 'NOT_CREATED'
launcherExitWait = $null
cleanup = @()
}
$run | Add-Member -NotePropertyName SeedReceipt -NotePropertyValue $receipt
$zmxSeedReceipts.Add($receipt)
$seedReceiptPath = Join-Path $runDirectory "phases\$Phase\normal\zmx-seed.json"
Write-CapJsonAtomic -Path $seedReceiptPath -Value $receipt
$receipt.receiptPublicationStatus = 'CREATED'
try { Write-CapJsonAtomic -Path $seedReceiptPath -Value $receipt }
catch {
$receipt.receiptPublicationStatus = 'NOT_CREATED'
throw
}

$seedDeadline = [DateTime]::UtcNow.AddSeconds(20)
if ($seedDeadline -gt $leaseDeadlineUtc) { $seedDeadline = $leaseDeadlineUtc }
Expand Down Expand Up @@ -1000,13 +1021,68 @@ function Close-CapGraphCodeApp {
$AppRun.CleanupComplete = $true
}

function Write-CapSeedCleanupReceipt {
param([Parameter(Mandatory)]$SeedRun)
if ($SeedRun.SeedReceipt.receiptPublicationStatus -ceq 'NOT_CREATED') {
$SeedRun.SeedReceipt.cleanupStatus = 'CLEANUP_UNPROVEN'
$cleanupFailures.Add("Seed receipt not created: $($SeedRun.SeedReceiptPath)")
return
}
try { Write-CapJsonAtomic -Path $SeedRun.SeedReceiptPath -Value $SeedRun.SeedReceipt }
catch {
$SeedRun.SeedReceipt.cleanupStatus = 'CLEANUP_UNPROVEN'
throw
}
}

function Wait-CapSeedLauncherExit {
param([Parameter(Mandatory)]$SeedRun, [Parameter(Mandatory)]$Proof)
$identity = $SeedRun.Identity
$handle = $identity.Process.Handle
if (-not $SeedRun.PSObject.Properties['LauncherImageAttestation']) {
throw 'Seed launcher original image attestation is missing.'
}
$attestation = $SeedRun.LauncherImageAttestation
if ($attestation.pid -ne $identity.Pid -or $attestation.creationFileTimeUtc -ne $identity.CreationFileTimeUtc -or
$attestation.heldHandle -ne $handle.ToInt64() -or [string]::IsNullOrWhiteSpace($attestation.imagePath) -or
$attestation.imagePath -ine $identity.ExpectedImagePath) {
throw 'Seed launcher original image attestation differs from the held registration.'
}
$now = [Diagnostics.Stopwatch]::GetTimestamp()
if ($Proof.terminationBudgetMilliseconds -ne 5000 -or $Proof.terminationStartedTimestamp -le 0 -or
$Proof.terminationStartedTimestamp -gt $now) { throw 'Seed launcher termination-window evidence differs.' }
$deadlineTimestamp = $Proof.terminationStartedTimestamp + 5L * [Diagnostics.Stopwatch]::Frequency
$remaining = 0
$configuredTimestamp = 0L
$signalled = [CapNative]::WaitForExit($handle,$identity.Pid,$identity.CreationFileTimeUtc,$deadlineTimestamp,
[ref]$remaining,[ref]$configuredTimestamp)
$observed = [Diagnostics.Stopwatch]::GetTimestamp()
return [pscustomobject]@{
kind = 'POST_TERMINATION_HELD_HANDLE_EXIT_WAIT'
pid = $identity.Pid; creationFileTimeUtc = $identity.CreationFileTimeUtc
heldHandle = $handle.ToInt64(); signalled = $signalled
waitBudgetRemainingMilliseconds = $remaining
terminationStartedTimestamp = $Proof.terminationStartedTimestamp
deadlineTimestamp = $deadlineTimestamp
stopwatchFrequency = [Diagnostics.Stopwatch]::Frequency
waitConfiguredTimestamp = $configuredTimestamp
terminationToSignalledMilliseconds = if ($signalled) {
($observed-$Proof.terminationStartedTimestamp) * 1000.0 / [Diagnostics.Stopwatch]::Frequency
} else { $null }
observedUtc = [DateTimeOffset]::UtcNow.ToString('o')
}
}

function Close-CapOfflineSeed {
param([Parameter(Mandatory)]$SeedRun)
Write-CapTrace -Command $MyInvocation.MyCommand.Name -Parameters $PSBoundParameters

if ($SeedRun.CleanupComplete) { return }
if ($SeedRun.PSObject.Properties['JobId']) {
if ($SeedRun.SeedCleanupComplete) { return }
$proof = Close-CapJobLauncher -Run $SeedRun
$launcherExitWait = Wait-CapSeedLauncherExit -SeedRun $SeedRun -Proof $proof
if ($SeedRun.PSObject.Properties['SeedReceipt']) { $SeedRun.SeedReceipt.launcherExitWait = $launcherExitWait }
if (-not $launcherExitWait.signalled) { throw 'Job member remains kernel-live.' }
foreach ($identity in $SeedRun.Members) {
if (-not @($descendantIdentities | Where-Object {
$_.Pid -eq $identity.Pid -and $_.CreationFileTimeUtc -eq $identity.CreationFileTimeUtc
Expand All @@ -1024,12 +1100,13 @@ function Close-CapOfflineSeed {
$SeedRun.SeedReceipt.cleanupStatus = 'CLEAN_ZERO_SURVIVORS_FILES_CLOSED'
# Target files are not raw redirected-stream EOF.
$SeedRun.SeedReceipt.stdoutEof = $false; $SeedRun.SeedReceipt.stderrEof = $false
$path = Join-Path $runDirectory "phases\$($SeedRun.Phase)\normal\zmx-seed.json"
Write-CapJsonAtomic -Path $path -Value $SeedRun.SeedReceipt
Write-CapSeedCleanupReceipt -SeedRun $SeedRun
}
Write-CapRunStreams -Run $SeedRun -Label "$($SeedRun.Phase)-zmx-launcher"
$SeedRun.SeedCleanupComplete = $true
return
}
if ($SeedRun.CleanupComplete) { return }
$failureCountBefore = $cleanupFailures.Count
$tree = @()
try {
Expand Down Expand Up @@ -1094,8 +1171,7 @@ function Close-CapOfflineSeed {
$cleanupFailures.Count -eq $failureCountBefore -and
$SeedRun.SeedReceipt.stdoutEof -and $SeedRun.SeedReceipt.stderrEof
) { 'CLEAN_ZERO_SURVIVORS_EOF' } else { 'CLEANUP_UNPROVEN' }
$seedReceiptPath = Join-Path $runDirectory "phases\$($SeedRun.Phase)\normal\zmx-seed.json"
try { Write-CapJsonAtomic -Path $seedReceiptPath -Value $SeedRun.SeedReceipt }
try { Write-CapSeedCleanupReceipt -SeedRun $SeedRun }
catch { $cleanupFailures.Add("Private zmx receipt update failed: $($_.Exception.Message)") }
if ($cleanupFailures.Count -eq $failureCountBefore) { $SeedRun.CleanupComplete = $true }
}
Expand Down
Loading
Loading