mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-26 12:56:21 +02:00
Release prep: 54 engines, self-hosted signatures, i18n, dashboard updates
This commit is contained in:
parent
694e32be26
commit
41cbfd6e0a
178 changed files with 36008 additions and 399 deletions
199
internal/infrastructure/tpmaudit/tpmaudit_test.go
Normal file
199
internal/infrastructure/tpmaudit/tpmaudit_test.go
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
package tpmaudit
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewSealedLogger(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
if logger.ChainLength() != 0 {
|
||||
t.Errorf("chain length = %d, want 0", logger.ChainLength())
|
||||
}
|
||||
|
||||
stats := logger.Stats()
|
||||
if stats.Mode != SealSoftware {
|
||||
t.Errorf("mode = %s, want software (no TPM in CI)", stats.Mode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogDecision(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
sealed, err := logger.LogDecision(DecisionEntry{
|
||||
Action: "ingest",
|
||||
Decision: "allow",
|
||||
Reason: "event passed secret scanner",
|
||||
EventID: "EVT-001",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("LogDecision: %v", err)
|
||||
}
|
||||
|
||||
if sealed.Hash == "" {
|
||||
t.Error("hash is empty")
|
||||
}
|
||||
if sealed.Signature == "" {
|
||||
t.Error("signature is empty")
|
||||
}
|
||||
if sealed.Entry.PreviousHash != "genesis" {
|
||||
t.Errorf("first entry previous_hash = %s, want genesis", sealed.Entry.PreviousHash)
|
||||
}
|
||||
if sealed.ChainIdx != 0 {
|
||||
t.Errorf("chain_idx = %d, want 0", sealed.ChainIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainLinking(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
s1, _ := logger.LogDecision(DecisionEntry{Action: "ingest", Decision: "allow", Reason: "ok"})
|
||||
s2, _ := logger.LogDecision(DecisionEntry{Action: "correlate", Decision: "escalate", Reason: "high severity"})
|
||||
s3, _ := logger.LogDecision(DecisionEntry{Action: "respond", Decision: "allow", Reason: "playbook matched"})
|
||||
|
||||
// Verify chain links.
|
||||
if s2.Entry.PreviousHash != s1.Hash {
|
||||
t.Error("entry 2 not linked to entry 1")
|
||||
}
|
||||
if s3.Entry.PreviousHash != s2.Hash {
|
||||
t.Error("entry 3 not linked to entry 2")
|
||||
}
|
||||
|
||||
if logger.ChainLength() != 3 {
|
||||
t.Errorf("chain length = %d, want 3", logger.ChainLength())
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyChain_Valid(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
logger.LogDecision(DecisionEntry{Action: "ingest", Decision: "allow", Reason: "ok"})
|
||||
logger.LogDecision(DecisionEntry{Action: "correlate", Decision: "allow", Reason: "ok"})
|
||||
logger.LogDecision(DecisionEntry{Action: "respond", Decision: "allow", Reason: "ok"})
|
||||
|
||||
result := logger.VerifyChain()
|
||||
if !result.Valid {
|
||||
t.Errorf("chain invalid: %s at index %d", result.BrokenReason, result.BrokenAtIndex)
|
||||
}
|
||||
if result.VerifiedCount != 3 {
|
||||
t.Errorf("verified = %d, want 3", result.VerifiedCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyChain_Tampered(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
logger.LogDecision(DecisionEntry{Action: "ingest", Decision: "allow", Reason: "ok"})
|
||||
logger.LogDecision(DecisionEntry{Action: "correlate", Decision: "allow", Reason: "ok"})
|
||||
|
||||
// Tamper with chain.
|
||||
logger.chain[1].Hash = "tampered-hash"
|
||||
|
||||
result := logger.VerifyChain()
|
||||
if result.Valid {
|
||||
t.Error("expected chain to be invalid after tampering")
|
||||
}
|
||||
if result.BrokenAtIndex != 1 {
|
||||
t.Errorf("broken at = %d, want 1", result.BrokenAtIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPCRExtension(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
s1, _ := logger.LogDecision(DecisionEntry{Action: "a", Decision: "allow", Reason: "ok"})
|
||||
s2, _ := logger.LogDecision(DecisionEntry{Action: "b", Decision: "allow", Reason: "ok"})
|
||||
|
||||
// PCR values should be different (extended with each entry).
|
||||
if s1.PCRValue == s2.PCRValue {
|
||||
t.Error("PCR values should differ after extension")
|
||||
}
|
||||
// PCR should not be the initial zero value.
|
||||
if s1.PCRValue == "0000000000000000000000000000000000000000000000000000000000000000" {
|
||||
t.Error("PCR should have been extended from zero")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersistence(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// Write entries.
|
||||
{
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
logger.LogDecision(DecisionEntry{Action: "ingest", Decision: "allow", Reason: "ok"})
|
||||
logger.LogDecision(DecisionEntry{Action: "correlate", Decision: "deny", Reason: "blocked"})
|
||||
logger.Close()
|
||||
}
|
||||
|
||||
// Reopen and verify chain was loaded.
|
||||
{
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger reopen: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
if logger.ChainLength() != 2 {
|
||||
t.Errorf("chain length after reopen = %d, want 2", logger.ChainLength())
|
||||
}
|
||||
}
|
||||
|
||||
// Verify file exists.
|
||||
if _, err := os.Stat(dir + "/decisions_sealed.jsonl"); err != nil {
|
||||
t.Errorf("log file not found: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStats(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
logger, err := NewSealedLogger(dir, "test-secret")
|
||||
if err != nil {
|
||||
t.Fatalf("NewSealedLogger: %v", err)
|
||||
}
|
||||
defer logger.Close()
|
||||
|
||||
logger.LogDecision(DecisionEntry{Action: "a", Decision: "allow", Reason: "ok"})
|
||||
logger.LogDecision(DecisionEntry{Action: "b", Decision: "deny", Reason: "blocked"})
|
||||
|
||||
stats := logger.Stats()
|
||||
if stats.TotalEntries != 2 {
|
||||
t.Errorf("total_entries = %d, want 2", stats.TotalEntries)
|
||||
}
|
||||
if !stats.ChainIntegrity {
|
||||
t.Error("chain integrity should be true")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue