mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-26 04:46:22 +02:00
262 lines
7.3 KiB
Go
262 lines
7.3 KiB
Go
// Package formalspec implements SEC-012 TLA+ Formal Verification.
|
|
//
|
|
// Provides a Go representation of the Event Bus pipeline and
|
|
// Decision Logger chain specifications for formal verification.
|
|
//
|
|
// The TLA+ specifications can be model-checked with the TLC checker:
|
|
// tlc EventBusPipeline.tla
|
|
// tlc DecisionLoggerChain.tla
|
|
//
|
|
// This package provides:
|
|
// - Go types mirroring the TLA+ state machine
|
|
// - Invariant checking functions
|
|
// - Trace generation for debugging
|
|
package formalspec
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// --- Event Bus Pipeline Specification ---
|
|
|
|
// PipelineState represents the Event Bus pipeline state machine.
|
|
type PipelineState string
|
|
|
|
const (
|
|
StateInit PipelineState = "INIT"
|
|
StateScanning PipelineState = "SCANNING" // Secret Scanner (Step 0)
|
|
StateDedup PipelineState = "DEDUP" // Deduplication
|
|
StateCorrelate PipelineState = "CORRELATE" // Correlation Engine
|
|
StatePersist PipelineState = "PERSIST" // SQLite Persist
|
|
StateDecisionLog PipelineState = "DECISION_LOG" // Audit Decision Logger
|
|
StateComplete PipelineState = "COMPLETE"
|
|
StateError PipelineState = "ERROR"
|
|
)
|
|
|
|
// Transition represents a state transition in the pipeline.
|
|
type Transition struct {
|
|
From PipelineState `json:"from"`
|
|
To PipelineState `json:"to"`
|
|
Guard string `json:"guard"` // Condition for transition
|
|
Action string `json:"action"` // Side effect
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
// PipelineSpec defines all valid transitions in the Event Bus pipeline.
|
|
var PipelineSpec = []Transition{
|
|
{From: StateInit, To: StateScanning, Guard: "event_received", Action: "run_secret_scanner"},
|
|
{From: StateScanning, To: StateDedup, Guard: "no_secrets_found", Action: "dedup_check"},
|
|
{From: StateScanning, To: StateError, Guard: "secret_detected", Action: "reject_event"},
|
|
{From: StateDedup, To: StateCorrelate, Guard: "not_duplicate", Action: "run_correlation"},
|
|
{From: StateDedup, To: StateComplete, Guard: "is_duplicate", Action: "skip"},
|
|
{From: StateCorrelate, To: StatePersist, Guard: "correlation_done", Action: "persist_event"},
|
|
{From: StatePersist, To: StateDecisionLog, Guard: "persisted", Action: "log_decision"},
|
|
{From: StateDecisionLog, To: StateComplete, Guard: "logged", Action: "emit_complete"},
|
|
}
|
|
|
|
// PipelineInvariant defines a safety property that must always hold.
|
|
type PipelineInvariant struct {
|
|
Name string
|
|
Description string
|
|
Check func(state PipelineState, history []Transition) bool
|
|
}
|
|
|
|
// PipelineInvariants are the safety properties of the Event Bus.
|
|
var PipelineInvariants = []PipelineInvariant{
|
|
{
|
|
Name: "SecretScannerAlwaysFirst",
|
|
Description: "Secret Scanner (Step 0) MUST execute before any other processing",
|
|
Check: func(state PipelineState, history []Transition) bool {
|
|
if len(history) == 0 {
|
|
return true
|
|
}
|
|
return history[0].From == StateInit && history[0].To == StateScanning
|
|
},
|
|
},
|
|
{
|
|
Name: "DecisionLoggerAlwaysFires",
|
|
Description: "Every successfully processed event MUST have a decision log entry",
|
|
Check: func(state PipelineState, history []Transition) bool {
|
|
if state != StateComplete {
|
|
return true // Only check on completion.
|
|
}
|
|
for _, t := range history {
|
|
if t.To == StateDecisionLog {
|
|
return true
|
|
}
|
|
}
|
|
// Allow completion from dedup (skip path).
|
|
for _, t := range history {
|
|
if t.Guard == "is_duplicate" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
},
|
|
},
|
|
{
|
|
Name: "NoSkipToComplete",
|
|
Description: "Cannot jump directly from INIT to COMPLETE",
|
|
Check: func(state PipelineState, history []Transition) bool {
|
|
for _, t := range history {
|
|
if t.From == StateInit && t.To == StateComplete {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
},
|
|
}
|
|
|
|
// --- Decision Logger Chain Specification ---
|
|
|
|
// ChainInvariant defines a safety property for the Decision Logger chain.
|
|
type ChainInvariant struct {
|
|
Name string
|
|
Description string
|
|
Check func(chain []ChainEntry) bool
|
|
}
|
|
|
|
// ChainEntry is a simplified chain entry for verification.
|
|
type ChainEntry struct {
|
|
Index int `json:"index"`
|
|
Hash string `json:"hash"`
|
|
PreviousHash string `json:"previous_hash"`
|
|
Signature string `json:"signature"`
|
|
}
|
|
|
|
// ChainInvariants are the safety properties of the Decision Logger.
|
|
var ChainInvariants = []ChainInvariant{
|
|
{
|
|
Name: "GenesisBlockValid",
|
|
Description: "First entry MUST have PreviousHash='genesis'",
|
|
Check: func(chain []ChainEntry) bool {
|
|
if len(chain) == 0 {
|
|
return true
|
|
}
|
|
return chain[0].PreviousHash == "genesis"
|
|
},
|
|
},
|
|
{
|
|
Name: "ChainContinuity",
|
|
Description: "Each entry[i].PreviousHash MUST equal entry[i-1].Hash",
|
|
Check: func(chain []ChainEntry) bool {
|
|
for i := 1; i < len(chain); i++ {
|
|
if chain[i].PreviousHash != chain[i-1].Hash {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
},
|
|
{
|
|
Name: "NoEmptyHashes",
|
|
Description: "No entry may have an empty hash",
|
|
Check: func(chain []ChainEntry) bool {
|
|
for _, e := range chain {
|
|
if e.Hash == "" {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
},
|
|
{
|
|
Name: "MonotonicIndices",
|
|
Description: "Chain indices MUST be strictly monotonically increasing",
|
|
Check: func(chain []ChainEntry) bool {
|
|
for i := 1; i < len(chain); i++ {
|
|
if chain[i].Index != chain[i-1].Index+1 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
},
|
|
}
|
|
|
|
// --- Verifier ---
|
|
|
|
// SpecVerifier runs formal invariant checks.
|
|
type SpecVerifier struct {
|
|
mu sync.Mutex
|
|
logger *slog.Logger
|
|
stats VerifierStats
|
|
}
|
|
|
|
// VerifierStats tracks verification results.
|
|
type VerifierStats struct {
|
|
TotalChecks int64 `json:"total_checks"`
|
|
Passed int64 `json:"passed"`
|
|
Failed int64 `json:"failed"`
|
|
}
|
|
|
|
// InvariantResult is the outcome of an invariant check.
|
|
type InvariantResult struct {
|
|
Name string `json:"name"`
|
|
Passed bool `json:"passed"`
|
|
Details string `json:"details,omitempty"`
|
|
}
|
|
|
|
// NewSpecVerifier creates a new formal spec verifier.
|
|
func NewSpecVerifier() *SpecVerifier {
|
|
return &SpecVerifier{
|
|
logger: slog.Default().With("component", "sec-012-formalspec"),
|
|
}
|
|
}
|
|
|
|
// VerifyPipeline checks all Event Bus pipeline invariants.
|
|
func (v *SpecVerifier) VerifyPipeline(state PipelineState, history []Transition) []InvariantResult {
|
|
var results []InvariantResult
|
|
for _, inv := range PipelineInvariants {
|
|
v.mu.Lock()
|
|
v.stats.TotalChecks++
|
|
passed := inv.Check(state, history)
|
|
if passed {
|
|
v.stats.Passed++
|
|
} else {
|
|
v.stats.Failed++
|
|
}
|
|
v.mu.Unlock()
|
|
|
|
results = append(results, InvariantResult{
|
|
Name: inv.Name,
|
|
Passed: passed,
|
|
Details: fmt.Sprintf("%s: %v", inv.Description, passed),
|
|
})
|
|
}
|
|
return results
|
|
}
|
|
|
|
// VerifyChain checks all Decision Logger chain invariants.
|
|
func (v *SpecVerifier) VerifyChain(chain []ChainEntry) []InvariantResult {
|
|
var results []InvariantResult
|
|
for _, inv := range ChainInvariants {
|
|
v.mu.Lock()
|
|
v.stats.TotalChecks++
|
|
passed := inv.Check(chain)
|
|
if passed {
|
|
v.stats.Passed++
|
|
} else {
|
|
v.stats.Failed++
|
|
}
|
|
v.mu.Unlock()
|
|
|
|
results = append(results, InvariantResult{
|
|
Name: inv.Name,
|
|
Passed: passed,
|
|
Details: fmt.Sprintf("%s: %v", inv.Description, passed),
|
|
})
|
|
}
|
|
return results
|
|
}
|
|
|
|
// Stats returns verification metrics.
|
|
func (v *SpecVerifier) Stats() VerifierStats {
|
|
v.mu.Lock()
|
|
defer v.mu.Unlock()
|
|
return v.stats
|
|
}
|