Files
git.stella-ops.org/demos/binary-micro-witness/verify.ps1

205 lines
6.9 KiB
PowerShell

# Binary Micro-Witness Verification Script
# Sprint: SPRINT_0128_001_BinaryIndex_binary_micro_witness
# Usage: .\verify.ps1 [-WitnessPath <path>] [-VerboseOutput]
[CmdletBinding()]
param(
[Parameter(Position=0)]
[string]$WitnessPath,
[switch]$All,
[switch]$VerboseOutput
)
$ErrorActionPreference = "Stop"
$Script:PassCount = 0
$Script:FailCount = 0
function Write-Status {
param([string]$Status, [string]$Message, [string]$Color = "White")
$symbol = switch ($Status) {
"PASS" { "[OK]"; $Color = "Green" }
"FAIL" { "[FAIL]"; $Color = "Red" }
"SKIP" { "[SKIP]"; $Color = "Yellow" }
"INFO" { "[INFO]"; $Color = "Cyan" }
default { "[-]" }
}
Write-Host "$symbol " -ForegroundColor $Color -NoNewline
Write-Host $Message
}
function Verify-Witness {
param([string]$Path)
Write-Host "`n=== Verifying: $Path ===" -ForegroundColor Cyan
if (-not (Test-Path $Path)) {
Write-Status "FAIL" "Witness file not found: $Path"
$Script:FailCount++
return
}
try {
$witness = Get-Content $Path -Raw | ConvertFrom-Json
} catch {
Write-Status "FAIL" "Failed to parse witness JSON: $_"
$Script:FailCount++
return
}
# Handle both standalone predicate and in-toto statement formats
$predicate = if ($witness.predicate) { $witness.predicate } else { $witness }
# Verify required fields
$requiredFields = @("schemaVersion", "binary", "cve", "verdict", "confidence", "tooling", "computedAt")
$missingFields = @()
foreach ($field in $requiredFields) {
if (-not $predicate.$field) {
$missingFields += $field
}
}
if ($missingFields.Count -gt 0) {
Write-Status "FAIL" "Missing required fields: $($missingFields -join ', ')"
$Script:FailCount++
return
}
Write-Status "PASS" "All required fields present"
# Display witness details
Write-Host "`nWitness Details:" -ForegroundColor White
Write-Host " Binary Digest: $($predicate.binary.digest)"
Write-Host " Binary File: $($predicate.binary.filename)"
Write-Host " CVE: $($predicate.cve.id)"
Write-Host " Verdict: $($predicate.verdict)"
Write-Host " Confidence: $([math]::Round($predicate.confidence * 100, 1))%"
Write-Host " Computed At: $($predicate.computedAt)"
# Verify schema version
if ($predicate.schemaVersion -eq "1.0.0") {
Write-Status "PASS" "Schema version: $($predicate.schemaVersion)"
} else {
Write-Status "SKIP" "Unknown schema version: $($predicate.schemaVersion)"
}
# Verify binary digest format
if ($predicate.binary.digest -match "^sha256:[a-fA-F0-9]{64}$") {
Write-Status "PASS" "Binary digest format valid"
} else {
Write-Status "FAIL" "Invalid binary digest format"
$Script:FailCount++
return
}
# Verify CVE ID format
if ($predicate.cve.id -match "^CVE-\d{4}-\d{4,}$") {
Write-Status "PASS" "CVE ID format valid"
} else {
Write-Status "SKIP" "Non-standard vulnerability ID: $($predicate.cve.id)"
}
# Verify verdict
$validVerdicts = @("patched", "vulnerable", "inconclusive", "partial")
if ($predicate.verdict -in $validVerdicts) {
$verdictColor = switch ($predicate.verdict) {
"patched" { "Green" }
"vulnerable" { "Red" }
"inconclusive" { "Yellow" }
"partial" { "Yellow" }
}
Write-Status "PASS" "Verdict: $($predicate.verdict)" -Color $verdictColor
} else {
Write-Status "FAIL" "Invalid verdict: $($predicate.verdict)"
$Script:FailCount++
return
}
# Verify confidence range
if ($predicate.confidence -ge 0 -and $predicate.confidence -le 1) {
Write-Status "PASS" "Confidence in valid range"
} else {
Write-Status "FAIL" "Confidence out of range: $($predicate.confidence)"
$Script:FailCount++
return
}
# Check evidence
if ($predicate.evidence -and $predicate.evidence.Count -gt 0) {
Write-Status "PASS" "Evidence present: $($predicate.evidence.Count) function(s)"
if ($VerboseOutput) {
Write-Host "`nFunction Evidence:" -ForegroundColor White
foreach ($ev in $predicate.evidence) {
$stateColor = switch ($ev.state) {
"patched" { "Green" }
"vulnerable" { "Red" }
"modified" { "Yellow" }
"unchanged" { "Gray" }
default { "White" }
}
Write-Host " - $($ev.function): " -NoNewline
Write-Host "$($ev.state)" -ForegroundColor $stateColor -NoNewline
Write-Host " (score: $([math]::Round($ev.score * 100, 1))%, method: $($ev.method))"
}
}
} else {
Write-Status "SKIP" "No function-level evidence provided"
}
# Check tooling
Write-Status "INFO" "Tooling: BinaryIndex $($predicate.tooling.binaryIndexVersion), Lifter: $($predicate.tooling.lifter)"
# TODO: Signature verification (not yet implemented)
Write-Status "SKIP" "Signature verification (not yet implemented)"
# TODO: Rekor proof verification (not yet implemented)
Write-Status "SKIP" "Rekor inclusion proof (not yet implemented)"
$Script:PassCount++
Write-Host "`nResult: " -NoNewline
Write-Host "VERIFIED" -ForegroundColor Green
}
# Main
Write-Host "Binary Micro-Witness Verification" -ForegroundColor Cyan
Write-Host "==================================" -ForegroundColor Cyan
Write-Host "Sprint: SPRINT_0128_001_BinaryIndex_binary_micro_witness"
Write-Host ""
if ($All) {
$witnessDir = Join-Path $PSScriptRoot "witnesses"
$witnesses = Get-ChildItem -Path $witnessDir -Filter "*.json" -ErrorAction SilentlyContinue
if ($witnesses.Count -eq 0) {
Write-Host "No witness files found in $witnessDir" -ForegroundColor Yellow
exit 1
}
foreach ($w in $witnesses) {
Verify-Witness -Path $w.FullName
}
} elseif ($WitnessPath) {
Verify-Witness -Path $WitnessPath
} else {
# Default: verify all witnesses
$witnessDir = Join-Path $PSScriptRoot "witnesses"
$witnesses = Get-ChildItem -Path $witnessDir -Filter "*.json" -ErrorAction SilentlyContinue
if ($witnesses.Count -eq 0) {
Write-Host "Usage: .\verify.ps1 [-WitnessPath <path>] [-All] [-Verbose]" -ForegroundColor Yellow
Write-Host ""
Write-Host "Examples:"
Write-Host " .\verify.ps1 witnesses\openssl-cve-2024-0567.json"
Write-Host " .\verify.ps1 -All -Verbose"
exit 1
}
foreach ($w in $witnesses) {
Verify-Witness -Path $w.FullName
}
}
Write-Host "`n==================================" -ForegroundColor Cyan
Write-Host "Summary: $Script:PassCount passed, $Script:FailCount failed" -ForegroundColor $(if ($Script:FailCount -eq 0) { "Green" } else { "Red" })
exit $Script:FailCount