$ErrorActionPreference = "Stop" Set-StrictMode -Version Latest $repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path Set-Location -LiteralPath $repo if (-not (Get-Command go -ErrorAction SilentlyContinue)) { throw "go was not found. Run this verifier in a Go 1.23.x environment." } Write-Host "## go test ./internal/uiaselector ./cmd/uia-selector-report" go test ./internal/uiaselector ./cmd/uia-selector-report -count=1 if ($LASTEXITCODE -ne 0) { throw "go tests failed with exit code $LASTEXITCODE" } $outDir = Join-Path $repo "runs\n14-selector-report" New-Item -ItemType Directory -Force -Path $outDir | Out-Null $jsonPath = Join-Path $outDir "n12r-selector-report.json" $mdPath = Join-Path $outDir "n12r-selector-report.md" Remove-Item -LiteralPath $jsonPath, $mdPath -Force -ErrorAction SilentlyContinue Write-Host "## go run ./cmd/uia-selector-report" go run ./cmd/uia-selector-report ` -dump internal/uiaselector/testdata/n12r-2026-07-09-uia-redacted.json ` -out-json $jsonPath ` -out-md $mdPath ` -strict if ($LASTEXITCODE -ne 0) { throw "uia-selector-report failed with exit code $LASTEXITCODE" } $jsonItem = Get-Item -LiteralPath $jsonPath -ErrorAction Stop if ($jsonItem.PSIsContainer) { throw "JSON report path is not a file: $jsonPath" } if ($jsonItem.Length -le 0) { throw "JSON report is empty: $jsonPath" } $mdItem = Get-Item -LiteralPath $mdPath -ErrorAction Stop if ($mdItem.PSIsContainer) { throw "Markdown report path is not a file: $mdPath" } if ($mdItem.Length -le 0) { throw "Markdown report is empty: $mdPath" } $report = Get-Content -LiteralPath $jsonPath -Raw -Encoding UTF8 | ConvertFrom-Json if (-not $report.ok) { throw "report ok=false" } if ($report.catalog_size -ne 10) { throw "catalog_size=$($report.catalog_size), want 10" } if ($report.matched_count -ne 10) { throw "matched_count=$($report.matched_count), want 10" } if ($report.unmatched_count -ne 0) { throw "unmatched_count=$($report.unmatched_count), want 0" } if ($report.ambiguous_count -ne 0) { throw "ambiguous_count=$($report.ambiguous_count), want 0" } Write-Host "## JSON match field allowlist" $allowedMatchFields = @( "path", "control_type", "automation_id", "class_name", "framework_id", "has_name", "name_length" ) $allowedMatchFieldLookup = @{} foreach ($field in $allowedMatchFields) { $allowedMatchFieldLookup[$field] = $true } foreach ($selector in @($report.selectors)) { foreach ($match in @($selector.matches)) { if ($null -eq $match) { continue } foreach ($property in @($match.PSObject.Properties)) { $fieldName = $property.Name if ($fieldName -eq "name" -or $fieldName -eq "value") { throw "JSON match leaks visible text field: $fieldName" } if (-not $allowedMatchFieldLookup.ContainsKey($fieldName)) { throw "JSON match contains disallowed field: $fieldName" } } } } Write-Host "## output safety scan" $jsonRaw = Get-Content -LiteralPath $jsonPath -Raw -Encoding UTF8 $mdRaw = Get-Content -LiteralPath $mdPath -Raw -Encoding UTF8 $forbiddenOutput = @( '"name"', '"value"', '国网甘肃省电力公司', 'send_after_approval', 'send_file_after_approval', 'receive_file_after_approval', 'open_conversation', 'InvokePattern', 'ValuePattern.SetValue' ) foreach ($term in $forbiddenOutput) { if ($jsonRaw.Contains($term)) { throw "JSON report contains forbidden term: $term" } if ($mdRaw.Contains($term)) { throw "Markdown report contains forbidden term: $term" } } Write-Host "N14 UIA selector report verification passed."