205 lines
6.9 KiB
PowerShell
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
|