mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-05-01 07:16:22 +02:00
215 lines
6.2 KiB
Go
215 lines
6.2 KiB
Go
package soc
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// ThreatIntelEngine implements §6 — IOC (Indicator of Compromise) matching.
|
|
// Maintains feed subscriptions and in-memory IOC database for real-time matching.
|
|
type ThreatIntelEngine struct {
|
|
mu sync.RWMutex
|
|
iocs map[string]*IOC // key = value (IP, domain, hash)
|
|
feeds []Feed
|
|
hits []IOCHit
|
|
max int
|
|
}
|
|
|
|
// IOCType categorizes the indicator.
|
|
type IOCType string
|
|
|
|
const (
|
|
IOCIP IOCType = "ip"
|
|
IOCDomain IOCType = "domain"
|
|
IOCHash IOCType = "hash"
|
|
IOCEmail IOCType = "email"
|
|
IOCURL IOCType = "url"
|
|
)
|
|
|
|
// IOC is an individual indicator of compromise.
|
|
type IOC struct {
|
|
Value string `json:"value"`
|
|
Type IOCType `json:"type"`
|
|
Severity string `json:"severity"` // CRITICAL, HIGH, MEDIUM, LOW
|
|
Source string `json:"source"` // Feed name
|
|
Tags []string `json:"tags"`
|
|
Description string `json:"description"`
|
|
FirstSeen time.Time `json:"first_seen"`
|
|
LastSeen time.Time `json:"last_seen"`
|
|
HitCount int `json:"hit_count"`
|
|
}
|
|
|
|
// Feed represents a threat intelligence source.
|
|
type Feed struct {
|
|
Name string `json:"name"`
|
|
URL string `json:"url"`
|
|
Type string `json:"type"` // stix, csv, json
|
|
Enabled bool `json:"enabled"`
|
|
IOCCount int `json:"ioc_count"`
|
|
LastSync time.Time `json:"last_sync"`
|
|
SyncInterval string `json:"sync_interval"`
|
|
}
|
|
|
|
// IOCHit records a match between an event and an IOC.
|
|
type IOCHit struct {
|
|
IOCValue string `json:"ioc_value"`
|
|
IOCType IOCType `json:"ioc_type"`
|
|
EventID string `json:"event_id"`
|
|
Severity string `json:"severity"`
|
|
Source string `json:"source"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
}
|
|
|
|
// NewThreatIntelEngine creates the IOC matching engine with default feeds.
|
|
func NewThreatIntelEngine() *ThreatIntelEngine {
|
|
t := &ThreatIntelEngine{
|
|
iocs: make(map[string]*IOC),
|
|
max: 1000,
|
|
}
|
|
t.loadDefaultFeeds()
|
|
t.loadSampleIOCs()
|
|
return t
|
|
}
|
|
|
|
func (t *ThreatIntelEngine) loadDefaultFeeds() {
|
|
t.feeds = []Feed{
|
|
{Name: "AlienVault OTX", URL: "https://otx.alienvault.com/api/v1/pulses/subscribed", Type: "json", Enabled: true, SyncInterval: "1h"},
|
|
{Name: "Abuse.ch URLhaus", URL: "https://urlhaus.abuse.ch/downloads/csv_recent/", Type: "csv", Enabled: true, SyncInterval: "30m"},
|
|
{Name: "CIRCL MISP", URL: "https://www.circl.lu/doc/misp/feed-osint/", Type: "stix", Enabled: false, SyncInterval: "6h"},
|
|
{Name: "Internal STIX", URL: "file:///var/sentinel/iocs/internal.stix", Type: "stix", Enabled: true, SyncInterval: "5m"},
|
|
}
|
|
}
|
|
|
|
func (t *ThreatIntelEngine) loadSampleIOCs() {
|
|
samples := []IOC{
|
|
{Value: "185.220.101.35", Type: IOCIP, Severity: "HIGH", Source: "AlienVault OTX", Tags: []string{"tor-exit", "scanner"}, Description: "Known Tor exit node / mass scanner"},
|
|
{Value: "evil-ai-jailbreak.com", Type: IOCDomain, Severity: "CRITICAL", Source: "Internal STIX", Tags: []string{"jailbreak", "c2"}, Description: "Jailbreak prompt C2 domain"},
|
|
{Value: "d41d8cd98f00b204e9800998ecf8427e", Type: IOCHash, Severity: "MEDIUM", Source: "Abuse.ch URLhaus", Tags: []string{"malware-hash"}, Description: "Known malware hash (MD5)"},
|
|
{Value: "attacker@malicious-prompts.org", Type: IOCEmail, Severity: "HIGH", Source: "Internal STIX", Tags: []string{"phishing", "social-engineering"}, Description: "Known prompt injection author"},
|
|
}
|
|
now := time.Now()
|
|
for _, ioc := range samples {
|
|
ioc := ioc // shadow to capture per-iteration (safe for Go <1.22)
|
|
ioc.FirstSeen = now.Add(-72 * time.Hour)
|
|
ioc.LastSeen = now
|
|
t.iocs[ioc.Value] = &ioc
|
|
}
|
|
for i := range t.feeds {
|
|
if t.feeds[i].Enabled {
|
|
t.feeds[i].IOCCount = len(samples) / 2
|
|
t.feeds[i].LastSync = now.Add(-15 * time.Minute)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Match checks a string against the IOC database.
|
|
// Returns matching IOC or nil.
|
|
func (t *ThreatIntelEngine) Match(value string) *IOC {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
normalized := strings.ToLower(strings.TrimSpace(value))
|
|
if ioc, ok := t.iocs[normalized]; ok {
|
|
ioc.HitCount++
|
|
ioc.LastSeen = time.Now()
|
|
copy := *ioc // return safe copy, not mutable internal pointer
|
|
return ©
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MatchEvent checks all fields of an event description for IOC matches.
|
|
// Returns all hits.
|
|
func (t *ThreatIntelEngine) MatchEvent(eventID, text string) []IOCHit {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
var hits []IOCHit
|
|
lower := strings.ToLower(text)
|
|
for _, ioc := range t.iocs {
|
|
if strings.Contains(lower, strings.ToLower(ioc.Value)) {
|
|
hit := IOCHit{
|
|
IOCValue: ioc.Value,
|
|
IOCType: ioc.Type,
|
|
EventID: eventID,
|
|
Severity: ioc.Severity,
|
|
Source: ioc.Source,
|
|
Timestamp: time.Now(),
|
|
}
|
|
ioc.HitCount++
|
|
ioc.LastSeen = time.Now()
|
|
hits = append(hits, hit)
|
|
|
|
if len(t.hits) >= t.max {
|
|
copy(t.hits, t.hits[1:])
|
|
t.hits[len(t.hits)-1] = hit
|
|
} else {
|
|
t.hits = append(t.hits, hit)
|
|
}
|
|
}
|
|
}
|
|
return hits
|
|
}
|
|
|
|
// AddIOC adds a custom indicator of compromise.
|
|
func (t *ThreatIntelEngine) AddIOC(ioc IOC) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
if ioc.FirstSeen.IsZero() {
|
|
ioc.FirstSeen = time.Now()
|
|
}
|
|
ioc.LastSeen = time.Now()
|
|
t.iocs[strings.ToLower(ioc.Value)] = &ioc
|
|
}
|
|
|
|
// ListIOCs returns all indicators.
|
|
func (t *ThreatIntelEngine) ListIOCs() []IOC {
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
result := make([]IOC, 0, len(t.iocs))
|
|
for _, ioc := range t.iocs {
|
|
result = append(result, *ioc)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// ListFeeds returns configured threat intel feeds.
|
|
func (t *ThreatIntelEngine) ListFeeds() []Feed {
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
result := make([]Feed, len(t.feeds))
|
|
copy(result, t.feeds)
|
|
return result
|
|
}
|
|
|
|
// RecentHits returns recent IOC match hits.
|
|
func (t *ThreatIntelEngine) RecentHits(limit int) []IOCHit {
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
if limit <= 0 || limit > len(t.hits) {
|
|
limit = len(t.hits)
|
|
}
|
|
start := len(t.hits) - limit
|
|
result := make([]IOCHit, limit)
|
|
copy(result, t.hits[start:])
|
|
return result
|
|
}
|
|
|
|
// Stats returns threat intel statistics.
|
|
func (t *ThreatIntelEngine) ThreatIntelStats() map[string]any {
|
|
t.mu.RLock()
|
|
defer t.mu.RUnlock()
|
|
enabledFeeds := 0
|
|
for _, f := range t.feeds {
|
|
if f.Enabled {
|
|
enabledFeeds++
|
|
}
|
|
}
|
|
return map[string]any{
|
|
"total_iocs": len(t.iocs),
|
|
"total_feeds": len(t.feeds),
|
|
"enabled_feeds": enabledFeeds,
|
|
"total_hits": len(t.hits),
|
|
}
|
|
}
|