mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-25 04:16:22 +02:00
145 lines
4.6 KiB
Go
145 lines
4.6 KiB
Go
package soc
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCorrelateMultistageJailbreak(t *testing.T) {
|
|
now := time.Now()
|
|
events := []SOCEvent{
|
|
{ID: "e1", Source: SourceSentinelCore, Category: "jailbreak", Severity: SeverityHigh, Timestamp: now.Add(-2 * time.Minute)},
|
|
{ID: "e2", Source: SourceSentinelCore, Category: "tool_abuse", Severity: SeverityHigh, Timestamp: now.Add(-1 * time.Minute)},
|
|
}
|
|
|
|
rules := DefaultSOCCorrelationRules()
|
|
matches := CorrelateSOCEvents(events, rules)
|
|
|
|
found := false
|
|
for _, m := range matches {
|
|
if m.Rule.ID == "SOC-CR-001" {
|
|
found = true
|
|
if len(m.Events) < 2 {
|
|
t.Errorf("expected at least 2 matched events, got %d", len(m.Events))
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("SOC-CR-001 (Multi-stage Jailbreak) should have matched")
|
|
}
|
|
}
|
|
|
|
func TestCorrelateOutsideWindow(t *testing.T) {
|
|
now := time.Now()
|
|
// Events too far apart — outside 5-minute window.
|
|
events := []SOCEvent{
|
|
{ID: "e1", Source: SourceSentinelCore, Category: "jailbreak", Severity: SeverityHigh, Timestamp: now.Add(-10 * time.Minute)},
|
|
{ID: "e2", Source: SourceSentinelCore, Category: "tool_abuse", Severity: SeverityHigh, Timestamp: now.Add(-1 * time.Minute)},
|
|
}
|
|
|
|
rules := []SOCCorrelationRule{DefaultSOCCorrelationRules()[0]} // SOC-CR-001 only
|
|
matches := CorrelateSOCEvents(events, rules)
|
|
|
|
if len(matches) != 0 {
|
|
t.Error("should not match events outside the time window")
|
|
}
|
|
}
|
|
|
|
func TestCorrelateCoordinatedAttack(t *testing.T) {
|
|
now := time.Now()
|
|
events := []SOCEvent{
|
|
{ID: "e1", Source: SourceSentinelCore, Category: "jailbreak", Timestamp: now.Add(-3 * time.Minute)},
|
|
{ID: "e2", Source: SourceSentinelCore, Category: "injection", Timestamp: now.Add(-2 * time.Minute)},
|
|
{ID: "e3", Source: SourceSentinelCore, Category: "exfiltration", Timestamp: now.Add(-1 * time.Minute)},
|
|
}
|
|
|
|
rules := DefaultSOCCorrelationRules()
|
|
matches := CorrelateSOCEvents(events, rules)
|
|
|
|
found := false
|
|
for _, m := range matches {
|
|
if m.Rule.ID == "SOC-CR-002" {
|
|
found = true
|
|
if len(m.Events) < 3 {
|
|
t.Errorf("expected at least 3 matched events, got %d", len(m.Events))
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("SOC-CR-002 (Coordinated Attack) should have matched")
|
|
}
|
|
}
|
|
|
|
func TestCorrelateCoordinatedAttackDifferentSources(t *testing.T) {
|
|
now := time.Now()
|
|
// Events from different sources — should NOT match coordinated attack.
|
|
events := []SOCEvent{
|
|
{ID: "e1", Source: SourceSentinelCore, Category: "jailbreak", Timestamp: now.Add(-3 * time.Minute)},
|
|
{ID: "e2", Source: SourceShield, Category: "injection", Timestamp: now.Add(-2 * time.Minute)},
|
|
{ID: "e3", Source: SourceImmune, Category: "exfiltration", Timestamp: now.Add(-1 * time.Minute)},
|
|
}
|
|
|
|
rules := []SOCCorrelationRule{DefaultSOCCorrelationRules()[1]} // SOC-CR-002 only
|
|
matches := CorrelateSOCEvents(events, rules)
|
|
|
|
if len(matches) != 0 {
|
|
t.Error("coordinated attack should only match same-source events")
|
|
}
|
|
}
|
|
|
|
func TestCorrelatePrivilegeEscalation(t *testing.T) {
|
|
now := time.Now()
|
|
events := []SOCEvent{
|
|
{ID: "e1", Source: SourceGoMCP, Category: "auth_bypass", Timestamp: now.Add(-10 * time.Minute)},
|
|
{ID: "e2", Source: SourceGoMCP, Category: "exfiltration", Timestamp: now.Add(-2 * time.Minute)},
|
|
}
|
|
|
|
rules := DefaultSOCCorrelationRules()
|
|
matches := CorrelateSOCEvents(events, rules)
|
|
|
|
found := false
|
|
for _, m := range matches {
|
|
if m.Rule.ID == "SOC-CR-003" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("SOC-CR-003 (Privilege Escalation Chain) should have matched")
|
|
}
|
|
}
|
|
|
|
func TestCorrelateSortsBySeverity(t *testing.T) {
|
|
now := time.Now()
|
|
events := []SOCEvent{
|
|
{ID: "e1", Source: SourceGoMCP, Category: "prompt_injection", Timestamp: now.Add(-2 * time.Minute)},
|
|
{ID: "e2", Source: SourceGoMCP, Category: "jailbreak", Timestamp: now.Add(-1 * time.Minute)},
|
|
{ID: "e3", Source: SourceGoMCP, Category: "tool_abuse", Timestamp: now.Add(-1 * time.Minute)},
|
|
}
|
|
|
|
rules := DefaultSOCCorrelationRules()
|
|
matches := CorrelateSOCEvents(events, rules)
|
|
|
|
if len(matches) < 2 {
|
|
t.Fatalf("expected at least 2 matches, got %d", len(matches))
|
|
}
|
|
// First match should be CRITICAL (highest severity).
|
|
if matches[0].Rule.Severity != SeverityCritical {
|
|
t.Errorf("first match should be CRITICAL, got %s", matches[0].Rule.Severity)
|
|
}
|
|
}
|
|
|
|
func TestCorrelateEmptyInput(t *testing.T) {
|
|
if matches := CorrelateSOCEvents(nil, DefaultSOCCorrelationRules()); matches != nil {
|
|
t.Error("nil events should return nil")
|
|
}
|
|
if matches := CorrelateSOCEvents([]SOCEvent{}, nil); matches != nil {
|
|
t.Error("nil rules should return nil")
|
|
}
|
|
}
|
|
|
|
func TestDefaultRuleCount(t *testing.T) {
|
|
rules := DefaultSOCCorrelationRules()
|
|
if len(rules) != 15 {
|
|
t.Errorf("expected 15 default rules, got %d", len(rules))
|
|
}
|
|
}
|