| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- # Operator-owned test-application fault. Status is read-only; Block and Restore require typed confirmation.
- param(
- [Parameter(Mandatory = $true)][string]$Plan,
- [ValidateSet('Status', 'Block', 'Restore')][string]$Action = 'Status'
- )
- $ErrorActionPreference = 'Stop'
- Set-StrictMode -Version Latest
- $planPath = (Resolve-Path -LiteralPath $Plan).Path
- $spec = Get-Content -LiteralPath $planPath -Raw -Encoding UTF8 | ConvertFrom-Json
- if ($spec.schemaVersion -ne 1 -or $spec.runId -cnotmatch '^[a-f0-9]{24}$' -or
- $spec.ruleName -cne "DSH-Update-Qualification-$($spec.runId)" -or
- $spec.sha512Hex -cnotmatch '^[A-F0-9]{128}$' -or
- $spec.executable -notmatch '^[A-Za-z]:\\' -or
- [IO.Path]::GetFileName($spec.executable) -cne "DSH Update Test $($spec.runId).exe" -or
- [IO.Path]::GetFullPath($spec.executable) -cne $spec.executable) {
- throw 'Invalid test-only network plan; no network changes were made.'
- }
- $owner = "dsh-update-qualification:$($spec.runId):$($spec.sha512Hex)"
- $recordParent = Join-Path (Split-Path -Parent $planPath) 'records'
- [IO.Directory]::CreateDirectory($recordParent) | Out-Null
- $record = Join-Path $recordParent ([Guid]::NewGuid().ToString('N'))
- New-Item -ItemType Directory -Path $record | Out-Null
- $events = Join-Path $record 'events.jsonl'
- function Save-Event([string]$Stage, [object]$Data) {
- $line = @{ time = [DateTime]::UtcNow.ToString('o'); action = $Action; stage = $Stage; data = $Data } | ConvertTo-Json -Depth 6 -Compress
- $bytes = [Text.Encoding]::UTF8.GetBytes($line + "`n")
- $stream = [IO.File]::Open($events, [IO.FileMode]::Append, [IO.FileAccess]::Write, [IO.FileShare]::Read)
- try { $stream.Write($bytes, 0, $bytes.Length); $stream.Flush($true) } finally { $stream.Dispose() }
- }
- function Find-OwnedRule {
- $rules = @(Get-NetFirewallRule -PolicyStore PersistentStore | Where-Object { $_.Name -ceq $spec.ruleName })
- if ($rules.Count -gt 1) { throw 'Multiple rules match this run; manual inspection is required.' }
- if ($rules.Count -eq 0) { return $null }
- $rule = $rules[0]
- $filters = @($rule | Get-NetFirewallApplicationFilter)
- if ($rule.Description -cne $owner -or [string]$rule.Direction -ne 'Outbound' -or [string]$rule.Action -ne 'Block' -or
- $filters.Count -ne 1 -or $filters[0].Program -ine $spec.executable) {
- throw 'The existing rule is not owned by this exact test plan; it was not modified.'
- }
- return $rule
- }
- $success = $false
- Write-Output "NETWORK_FAULT_RECORD $record"
- try {
- Save-Event 'started' @{ runId = $spec.runId; executable = $spec.executable; ruleName = $spec.ruleName }
- $existing = Find-OwnedRule
- Save-Event 'before' @{ present = ($null -ne $existing) }
- if ($Action -eq 'Block') {
- if ($null -ne $existing) { throw 'A test rule already exists. Restore it before starting another fault.' }
- if ((Get-FileHash -LiteralPath $spec.executable -Algorithm SHA512).Hash -cne $spec.sha512Hex) {
- throw 'The test executable changed. No rule was created.'
- }
- Write-Output "Only this executable will be blocked: $($spec.executable)"
- Write-Output 'Keep a second administrator terminal ready to run Restore. Do not disable the adapter, VPN, or proxy.'
- if ((Read-Host "Type BLOCK $($spec.runId) after download progress begins") -cne "BLOCK $($spec.runId)") {
- throw 'Confirmation declined; no rule was created.'
- }
- if ($null -ne (Find-OwnedRule)) { throw 'A rule appeared during confirmation. No rule was created.' }
- if ((Get-FileHash -LiteralPath $spec.executable -Algorithm SHA512).Hash -cne $spec.sha512Hex) {
- throw 'The test executable changed during confirmation. No rule was created.'
- }
- Save-Event 'creating-rule' @{ name = $spec.ruleName }
- New-NetFirewallRule -Name $spec.ruleName -DisplayName $spec.ruleName -Description $owner `
- -Direction Outbound -Program $spec.executable -Action Block -Profile Any -Enabled True -PolicyStore PersistentStore | Out-Null
- $created = Find-OwnedRule
- if ($null -eq $created -or [string]$created.Enabled -ne 'True') { throw 'The rule was not confirmed enabled. Use Restore.' }
- Save-Event 'blocked-rule-present' @{ trafficInterruptionVerified = $false }
- } elseif ($Action -eq 'Restore') {
- if ($null -ne $existing) {
- if ((Read-Host "Type RESTORE $($spec.runId)") -cne "RESTORE $($spec.runId)") { throw 'Restoration declined.' }
- $existing = Find-OwnedRule
- if ($null -ne $existing) {
- Save-Event 'removing-rule' @{ name = $spec.ruleName }
- $existing | Remove-NetFirewallRule
- }
- }
- if ($null -ne (Find-OwnedRule)) { throw 'The test rule still exists; restoration is incomplete.' }
- Save-Event 'rule-absent' @{ downloadRecoveryVerified = $false }
- } else {
- Save-Event 'status' @{ present = ($null -ne $existing); enabled = $(if ($null -ne $existing) { [string]$existing.Enabled } else { $null }) }
- Write-Output "Test rule present: $($null -ne $existing)"
- }
- $success = $true
- } catch {
- Save-Event 'failed' @{ message = $_.Exception.Message; automaticRetry = $false }
- throw
- } finally {
- Save-Event 'finished' @{ success = $success; rulePresenceIsNotTrafficEvidence = $true }
- }
|