203 lines
9.0 KiB
PowerShell
203 lines
9.0 KiB
PowerShell
param(
|
|
[string]$PackagePath = "",
|
|
[string]$ZipPath = "",
|
|
[string]$WorkDir = "",
|
|
[switch]$Strict
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
Set-StrictMode -Version Latest
|
|
|
|
function New-StringFromCodes([int[]]$Codes) {
|
|
return -join ($Codes | ForEach-Object { [char]$_ })
|
|
}
|
|
|
|
$inputFileName = New-StringFromCodes @(0x586B,0x5199,0x2D,0x53D1,0x9001,0x4FE1,0x606F,0x2E,0x6A,0x73,0x6F,0x6E)
|
|
$summaryFileName = New-StringFromCodes @(0x53D1,0x9001,0x8BB0,0x5F55,0x8BCA,0x65AD,0x6458,0x8981,0x2E,0x6A,0x73,0x6F,0x6E)
|
|
|
|
function ConvertTo-Sha256Hex([string]$Text) {
|
|
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
|
|
$sha = [System.Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
return -join ($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") })
|
|
}
|
|
finally {
|
|
$sha.Dispose()
|
|
}
|
|
}
|
|
|
|
function Get-JsonPropertyValue($Object, [string]$Name, $Default = $null) {
|
|
if ($null -eq $Object) { return $Default }
|
|
$property = $Object.PSObject.Properties[$Name]
|
|
if ($null -eq $property) { return $Default }
|
|
return $property.Value
|
|
}
|
|
|
|
function Test-NonEmptyString($Value) {
|
|
if ($null -eq $Value) { return $false }
|
|
return -not [string]::IsNullOrWhiteSpace([string]$Value)
|
|
}
|
|
|
|
function ConvertTo-Bool($Value, [bool]$Default = $false) {
|
|
if ($null -eq $Value) { return $Default }
|
|
if ($Value -is [bool]) { return [bool]$Value }
|
|
$text = ([string]$Value).Trim().ToLowerInvariant()
|
|
if ($text -eq "true" -or $text -eq "1" -or $text -eq "yes") { return $true }
|
|
if ($text -eq "false" -or $text -eq "0" -or $text -eq "no") { return $false }
|
|
return $Default
|
|
}
|
|
|
|
function Get-RunCountFromEvidence($AllFiles, [string]$Phase) {
|
|
$pattern = "[\\/]return-evidence[\\/]$Phase[\\/]"
|
|
$manifestDirs = @($AllFiles | Where-Object { $_.FullName -match $pattern -and $_.Name -eq "manifest.json" } | ForEach-Object { $_.DirectoryName } | Sort-Object -Unique)
|
|
if ($manifestDirs.Count -gt 0) { return $manifestDirs.Count }
|
|
$phaseFiles = @($AllFiles | Where-Object { $_.FullName -match $pattern })
|
|
if ($phaseFiles.Count -gt 0) { return 1 }
|
|
return 0
|
|
}
|
|
|
|
function Test-AfterOfflineBlocked($AllFiles, $Summary) {
|
|
$summaryValue = Get-JsonPropertyValue $Summary "after_offline_blocked" $null
|
|
if ($null -ne $summaryValue) { return ConvertTo-Bool $summaryValue $false }
|
|
$afterFiles = @($AllFiles | Where-Object { $_.FullName -match "[\\/]return-evidence[\\/]after[\\/]" -and $_.Name -eq "send_uia_controls_preflight.json" })
|
|
foreach ($file in $afterFiles) {
|
|
$text = Get-Content -LiteralPath $file.FullName -Raw
|
|
if ($text.Contains("A_ROUTE_OFFLINE_BLOCKED") -or $text.Contains('"offline_blocker_visible":true')) {
|
|
return $true
|
|
}
|
|
}
|
|
return $false
|
|
}
|
|
|
|
function Get-ArrayValue($Object, [string]$Name) {
|
|
$value = Get-JsonPropertyValue $Object $Name @()
|
|
if ($null -eq $value) { return @() }
|
|
return @($value)
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($ZipPath)) {
|
|
$PackagePath = $ZipPath
|
|
}
|
|
if ([string]::IsNullOrWhiteSpace($PackagePath)) {
|
|
throw "PackagePath or ZipPath is required"
|
|
}
|
|
if (-not (Test-Path -LiteralPath $PackagePath)) {
|
|
throw "package not found"
|
|
}
|
|
|
|
$resolvedPackage = (Resolve-Path -LiteralPath $PackagePath).Path
|
|
$cleanup = $false
|
|
if ([string]::IsNullOrWhiteSpace($WorkDir)) {
|
|
$WorkDir = Join-Path ([System.IO.Path]::GetTempPath()) ("isphere-send-sent-diagnostic-" + [guid]::NewGuid().ToString("N"))
|
|
$cleanup = $true
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $WorkDir) {
|
|
Remove-Item -LiteralPath $WorkDir -Recurse -Force
|
|
}
|
|
New-Item -ItemType Directory -Force -Path $WorkDir | Out-Null
|
|
|
|
try {
|
|
if ($resolvedPackage.EndsWith(".zip", [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
Expand-Archive -LiteralPath $resolvedPackage -DestinationPath $WorkDir -Force
|
|
$root = $WorkDir
|
|
}
|
|
else {
|
|
$root = $resolvedPackage
|
|
}
|
|
|
|
$allFiles = @(Get-ChildItem -LiteralPath $root -Recurse -File)
|
|
$summaryFile = $allFiles | Where-Object { $_.Name -eq $summaryFileName } | Sort-Object FullName | Select-Object -First 1
|
|
$inputFile = $allFiles | Where-Object { $_.Name -eq $inputFileName } | Sort-Object FullName | Select-Object -First 1
|
|
|
|
$summaryJson = $null
|
|
if ($summaryFile) {
|
|
$summaryJson = Get-Content -LiteralPath $summaryFile.FullName -Raw | ConvertFrom-Json
|
|
}
|
|
$inputJson = $null
|
|
if ($inputFile) {
|
|
$inputJson = Get-Content -LiteralPath $inputFile.FullName -Raw | ConvertFrom-Json
|
|
}
|
|
|
|
$contentHashMatches = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "content_sha256_matches_input" $null) $false
|
|
if ($null -ne $inputJson -and (Test-NonEmptyString (Get-JsonPropertyValue $inputJson "content_text_to_send")) -and (Test-NonEmptyString (Get-JsonPropertyValue $inputJson "content_sha256"))) {
|
|
$contentHashMatches = ((ConvertTo-Sha256Hex ([string](Get-JsonPropertyValue $inputJson "content_text_to_send"))) -eq ([string](Get-JsonPropertyValue $inputJson "content_sha256")))
|
|
}
|
|
|
|
$beforeRunCount = [int](Get-JsonPropertyValue $summaryJson "before_run_count" (Get-RunCountFromEvidence $allFiles "before"))
|
|
$afterRunCount = [int](Get-JsonPropertyValue $summaryJson "after_run_count" (Get-RunCountFromEvidence $allFiles "after"))
|
|
$exactContentSeenAfter = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "exact_content_seen_after" $false) $false
|
|
$contentShaSeenAfter = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "content_sha256_seen_after" $false) $false
|
|
$afterOfflineBlocked = Test-AfterOfflineBlocked $allFiles $summaryJson
|
|
$operatorObservedSuccess = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "operator_observed_success" (Get-JsonPropertyValue $inputJson "operator_observed_success" $false)) $false
|
|
$noSecondSend = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "do_not_send_second_time_confirmed" (Get-JsonPropertyValue $inputJson "do_not_send_second_time_confirmed" $false)) $false
|
|
$productionSendEnabled = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "production_send_enabled" (Get-JsonPropertyValue $inputJson "production_send_enabled" $false)) $false
|
|
$automaticSendEnabled = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "automatic_send_enabled" (Get-JsonPropertyValue $inputJson "automatic_send_enabled" $false)) $false
|
|
$rawBodyWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "raw_message_body_written" $false) $false
|
|
$rawTargetWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "raw_target_id_written" $false) $false
|
|
$rawIdemWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "raw_idempotency_key_written" $false) $false
|
|
$localPathsWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "local_paths_written" $false) $false
|
|
|
|
$strictPass = (
|
|
$contentHashMatches -and
|
|
$beforeRunCount -ge 1 -and
|
|
$afterRunCount -ge 1 -and
|
|
($exactContentSeenAfter -or $contentShaSeenAfter) -and
|
|
-not $afterOfflineBlocked -and
|
|
$operatorObservedSuccess -and
|
|
$noSecondSend -and
|
|
-not $productionSendEnabled -and
|
|
-not $automaticSendEnabled -and
|
|
-not $rawBodyWritten -and
|
|
-not $rawTargetWritten -and
|
|
-not $rawIdemWritten -and
|
|
-not $localPathsWritten
|
|
)
|
|
|
|
$decision = "not_usable"
|
|
if ($strictPass) {
|
|
$decision = "strict_send_record_pass"
|
|
}
|
|
elseif ($operatorObservedSuccess -and $beforeRunCount -ge 1 -and $afterRunCount -ge 1 -and $contentHashMatches) {
|
|
$decision = "manual_success_without_machine_sent_record"
|
|
}
|
|
elseif ($beforeRunCount -ge 1 -or $afterRunCount -ge 1) {
|
|
$decision = "partial_probe_only"
|
|
}
|
|
|
|
$summaryOut = [ordered]@{
|
|
ok = $true
|
|
package_type = "send-sent-record-diagnostic"
|
|
package_file_name = [System.IO.Path]::GetFileName($resolvedPackage)
|
|
summary_json_present = ($null -ne $summaryFile)
|
|
input_json_present = ($null -ne $inputFile)
|
|
content_sha256_matches_input = $contentHashMatches
|
|
before_run_count = $beforeRunCount
|
|
after_run_count = $afterRunCount
|
|
exact_content_seen_after = $exactContentSeenAfter
|
|
content_sha256_seen_after = $contentShaSeenAfter
|
|
after_offline_blocked = $afterOfflineBlocked
|
|
latest_after_route_hints = Get-ArrayValue $summaryJson "latest_after_route_hints"
|
|
operator_observed_success = $operatorObservedSuccess
|
|
do_not_send_second_time_confirmed = $noSecondSend
|
|
production_send_enabled = $productionSendEnabled
|
|
automatic_send_enabled = $automaticSendEnabled
|
|
raw_message_body_written = $rawBodyWritten
|
|
raw_target_id_written = $rawTargetWritten
|
|
raw_idempotency_key_written = $rawIdemWritten
|
|
local_paths_written = $localPathsWritten
|
|
strict_send_record_pass = $strictPass
|
|
production_send_unlock_recommended = $strictPass
|
|
decision = $decision
|
|
}
|
|
$summaryOut | ConvertTo-Json -Depth 6 -Compress
|
|
if ($Strict -and -not $strictPass) {
|
|
exit 1
|
|
}
|
|
}
|
|
finally {
|
|
if ($cleanup -and (Test-Path -LiteralPath $WorkDir)) {
|
|
Remove-Item -LiteralPath $WorkDir -Recurse -Force
|
|
}
|
|
}
|