mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-05-05 01:02:37 +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
138
internal/domain/engines/engines.go
Normal file
138
internal/domain/engines/engines.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package engines
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EngineStatus represents the health state of a security engine.
|
||||
type EngineStatus string
|
||||
|
||||
const (
|
||||
EngineHealthy EngineStatus = "HEALTHY"
|
||||
EngineDegraded EngineStatus = "DEGRADED"
|
||||
EngineOffline EngineStatus = "OFFLINE"
|
||||
EngineInitializing EngineStatus = "INITIALIZING"
|
||||
)
|
||||
|
||||
// ScanResult is the unified output from any security engine.
|
||||
type ScanResult struct {
|
||||
Engine string `json:"engine"`
|
||||
ThreatFound bool `json:"threat_found"`
|
||||
ThreatType string `json:"threat_type,omitempty"`
|
||||
Severity string `json:"severity"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Details string `json:"details,omitempty"`
|
||||
Indicators []string `json:"indicators,omitempty"`
|
||||
Duration time.Duration `json:"duration_ns"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// SentinelCore defines the interface for the Rust-based detection engine (§3).
|
||||
// Real implementation: FFI bridge to sentinel-core Rust binary.
|
||||
// Stub implementation: used when sentinel-core is not deployed.
|
||||
type SentinelCore interface {
|
||||
// Name returns the engine identifier.
|
||||
Name() string
|
||||
|
||||
// Status returns current engine health.
|
||||
Status() EngineStatus
|
||||
|
||||
// ScanPrompt analyzes an LLM prompt for injection/jailbreak patterns.
|
||||
ScanPrompt(ctx context.Context, prompt string) (*ScanResult, error)
|
||||
|
||||
// ScanResponse analyzes an LLM response for data exfiltration or harmful content.
|
||||
ScanResponse(ctx context.Context, response string) (*ScanResult, error)
|
||||
|
||||
// Version returns the engine version.
|
||||
Version() string
|
||||
}
|
||||
|
||||
// Shield defines the interface for the C++ network protection engine (§4).
|
||||
// Real implementation: FFI bridge to shield C++ shared library.
|
||||
// Stub implementation: used when shield is not deployed.
|
||||
type Shield interface {
|
||||
// Name returns the engine identifier.
|
||||
Name() string
|
||||
|
||||
// Status returns current engine health.
|
||||
Status() EngineStatus
|
||||
|
||||
// InspectTraffic analyzes network traffic for threats.
|
||||
InspectTraffic(ctx context.Context, payload []byte, metadata map[string]string) (*ScanResult, error)
|
||||
|
||||
// BlockIP adds an IP to the block list.
|
||||
BlockIP(ctx context.Context, ip string, reason string, duration time.Duration) error
|
||||
|
||||
// ListBlocked returns currently blocked IPs.
|
||||
ListBlocked(ctx context.Context) ([]BlockedIP, error)
|
||||
|
||||
// Version returns the engine version.
|
||||
Version() string
|
||||
}
|
||||
|
||||
// BlockedIP represents a blocked IP entry.
|
||||
type BlockedIP struct {
|
||||
IP string `json:"ip"`
|
||||
Reason string `json:"reason"`
|
||||
BlockedAt time.Time `json:"blocked_at"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
// --- Stub implementations for standalone Go deployment ---
|
||||
|
||||
// StubSentinelCore is a no-op sentinel-core when Rust engine is not deployed.
|
||||
type StubSentinelCore struct{}
|
||||
|
||||
func NewStubSentinelCore() *StubSentinelCore { return &StubSentinelCore{} }
|
||||
func (s *StubSentinelCore) Name() string { return "sentinel-core-stub" }
|
||||
func (s *StubSentinelCore) Status() EngineStatus { return EngineOffline }
|
||||
func (s *StubSentinelCore) Version() string { return "stub-1.0" }
|
||||
|
||||
func (s *StubSentinelCore) ScanPrompt(_ context.Context, _ string) (*ScanResult, error) {
|
||||
return &ScanResult{
|
||||
Engine: "sentinel-core-stub",
|
||||
ThreatFound: false,
|
||||
Severity: "NONE",
|
||||
Confidence: 0,
|
||||
Details: "sentinel-core not deployed, stub mode",
|
||||
Timestamp: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *StubSentinelCore) ScanResponse(_ context.Context, _ string) (*ScanResult, error) {
|
||||
return &ScanResult{
|
||||
Engine: "sentinel-core-stub",
|
||||
ThreatFound: false,
|
||||
Severity: "NONE",
|
||||
Confidence: 0,
|
||||
Details: "sentinel-core not deployed, stub mode",
|
||||
Timestamp: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StubShield is a no-op shield when C++ engine is not deployed.
|
||||
type StubShield struct{}
|
||||
|
||||
func NewStubShield() *StubShield { return &StubShield{} }
|
||||
func (s *StubShield) Name() string { return "shield-stub" }
|
||||
func (s *StubShield) Status() EngineStatus { return EngineOffline }
|
||||
func (s *StubShield) Version() string { return "stub-1.0" }
|
||||
|
||||
func (s *StubShield) InspectTraffic(_ context.Context, _ []byte, _ map[string]string) (*ScanResult, error) {
|
||||
return &ScanResult{
|
||||
Engine: "shield-stub",
|
||||
ThreatFound: false,
|
||||
Severity: "NONE",
|
||||
Details: "shield not deployed, stub mode",
|
||||
Timestamp: time.Now(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *StubShield) BlockIP(_ context.Context, _ string, _ string, _ time.Duration) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StubShield) ListBlocked(_ context.Context) ([]BlockedIP, error) {
|
||||
return nil, nil
|
||||
}
|
||||
69
internal/domain/engines/engines_test.go
Normal file
69
internal/domain/engines/engines_test.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package engines
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStubSentinelCore(t *testing.T) {
|
||||
core := NewStubSentinelCore()
|
||||
|
||||
if core.Name() != "sentinel-core-stub" {
|
||||
t.Fatalf("expected stub name, got %s", core.Name())
|
||||
}
|
||||
if core.Status() != EngineOffline {
|
||||
t.Fatal("stub should be offline")
|
||||
}
|
||||
|
||||
result, err := core.ScanPrompt(context.Background(), "test prompt injection")
|
||||
if err != nil {
|
||||
t.Fatalf("scan should not error: %v", err)
|
||||
}
|
||||
if result.ThreatFound {
|
||||
t.Fatal("stub should never find threats")
|
||||
}
|
||||
if result.Engine != "sentinel-core-stub" {
|
||||
t.Fatalf("wrong engine: %s", result.Engine)
|
||||
}
|
||||
|
||||
result2, err := core.ScanResponse(context.Background(), "response data")
|
||||
if err != nil {
|
||||
t.Fatalf("response scan should not error: %v", err)
|
||||
}
|
||||
if result2.ThreatFound {
|
||||
t.Fatal("stub response scan should not find threats")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStubShield(t *testing.T) {
|
||||
shield := NewStubShield()
|
||||
|
||||
if shield.Name() != "shield-stub" {
|
||||
t.Fatalf("expected stub name, got %s", shield.Name())
|
||||
}
|
||||
if shield.Status() != EngineOffline {
|
||||
t.Fatal("stub should be offline")
|
||||
}
|
||||
|
||||
result, err := shield.InspectTraffic(context.Background(), []byte("data"), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("inspect should not error: %v", err)
|
||||
}
|
||||
if result.ThreatFound {
|
||||
t.Fatal("stub should never find threats")
|
||||
}
|
||||
|
||||
err = shield.BlockIP(context.Background(), "1.2.3.4", "test", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("block should not error: %v", err)
|
||||
}
|
||||
|
||||
blocked, err := shield.ListBlocked(context.Background())
|
||||
if err != nil || len(blocked) != 0 {
|
||||
t.Fatal("stub should return empty blocked list")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify interfaces are satisfied at compile time
|
||||
var _ SentinelCore = (*StubSentinelCore)(nil)
|
||||
var _ Shield = (*StubShield)(nil)
|
||||
123
internal/domain/engines/ffi_sentinel.go
Normal file
123
internal/domain/engines/ffi_sentinel.go
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
//go:build sentinel_native
|
||||
|
||||
package engines
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -L${SRCDIR}/../../../../sentinel-core/target/release -lsentinel_core
|
||||
#cgo CFLAGS: -I${SRCDIR}/../../../../sentinel-core/include
|
||||
|
||||
// sentinel_core.h — C-compatible FFI interface for Rust sentinel-core.
|
||||
// These declarations match the Rust #[no_mangle] extern "C" functions.
|
||||
//
|
||||
// Build sentinel-core:
|
||||
// cd sentinel-core && cargo build --release
|
||||
//
|
||||
// The library exposes:
|
||||
// sentinel_init() — Initialize the engine
|
||||
// sentinel_analyze() — Analyze text for jailbreak/injection patterns
|
||||
// sentinel_status() — Get engine health status
|
||||
// sentinel_shutdown() — Graceful shutdown
|
||||
|
||||
// Stub declarations for build without native library.
|
||||
// When building WITH sentinel-core, replace stubs with actual FFI.
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NativeSentinelCore wraps the Rust sentinel-core via CGo FFI.
|
||||
// Build tag: sentinel_native
|
||||
//
|
||||
// When sentinel-core.so/dylib is not available, the StubSentinelCore
|
||||
// is used automatically (see engines.go).
|
||||
type NativeSentinelCore struct {
|
||||
mu sync.RWMutex
|
||||
initialized bool
|
||||
version string
|
||||
lastCheck time.Time
|
||||
}
|
||||
|
||||
// NewNativeSentinelCore creates the FFI bridge.
|
||||
// Returns error if the native library is not available.
|
||||
func NewNativeSentinelCore() (*NativeSentinelCore, error) {
|
||||
n := &NativeSentinelCore{
|
||||
version: "0.1.0-ffi",
|
||||
}
|
||||
|
||||
// TODO: Call C.sentinel_init() when native library is available
|
||||
// result := C.sentinel_init()
|
||||
// if result != 0 {
|
||||
// return nil, fmt.Errorf("sentinel_init failed: %d", result)
|
||||
// }
|
||||
|
||||
n.initialized = true
|
||||
n.lastCheck = time.Now()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Analyze sends text through the sentinel-core analysis pipeline.
|
||||
// Returns: confidence (0-1), detected categories, is_threat flag.
|
||||
func (n *NativeSentinelCore) Analyze(text string) SentinelResult {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if !n.initialized {
|
||||
return SentinelResult{Error: "engine not initialized"}
|
||||
}
|
||||
|
||||
// TODO: FFI call
|
||||
// cText := C.CString(text)
|
||||
// defer C.free(unsafe.Pointer(cText))
|
||||
// result := C.sentinel_analyze(cText)
|
||||
|
||||
// Stub analysis for now
|
||||
return SentinelResult{
|
||||
Confidence: 0.0,
|
||||
Categories: []string{},
|
||||
IsThreat: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Status returns the engine health via FFI.
|
||||
func (n *NativeSentinelCore) Status() EngineStatus {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if !n.initialized {
|
||||
return EngineOffline
|
||||
}
|
||||
|
||||
// TODO: Call C.sentinel_status()
|
||||
return EngineHealthy
|
||||
}
|
||||
|
||||
// Name returns the engine identifier.
|
||||
func (n *NativeSentinelCore) Name() string {
|
||||
return "sentinel-core"
|
||||
}
|
||||
|
||||
// Version returns the native library version.
|
||||
func (n *NativeSentinelCore) Version() string {
|
||||
return n.version
|
||||
}
|
||||
|
||||
// Shutdown gracefully closes the FFI bridge.
|
||||
func (n *NativeSentinelCore) Shutdown() error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
// TODO: C.sentinel_shutdown()
|
||||
n.initialized = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// SentinelResult is returned by the Analyze function.
|
||||
type SentinelResult struct {
|
||||
Confidence float64 `json:"confidence"`
|
||||
Categories []string `json:"categories"`
|
||||
IsThreat bool `json:"is_threat"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
108
internal/domain/engines/ffi_shield.go
Normal file
108
internal/domain/engines/ffi_shield.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
//go:build shield_native
|
||||
|
||||
package engines
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -L${SRCDIR}/../../../../shield/build -lshield
|
||||
#cgo CFLAGS: -I${SRCDIR}/../../../../shield/include
|
||||
|
||||
// shield.h — C-compatible FFI interface for C++ shield engine.
|
||||
// These declarations match the extern "C" functions from shield.
|
||||
//
|
||||
// Build shield:
|
||||
// cd shield && mkdir build && cd build && cmake .. && make
|
||||
//
|
||||
// The library exposes:
|
||||
// shield_init() — Initialize the network protection engine
|
||||
// shield_inspect() — Deep packet inspection / prompt filtering
|
||||
// shield_status() — Get engine health
|
||||
// shield_shutdown() — Graceful shutdown
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NativeShield wraps the C++ shield engine via CGo FFI.
|
||||
// Build tag: shield_native
|
||||
type NativeShield struct {
|
||||
mu sync.RWMutex
|
||||
initialized bool
|
||||
version string
|
||||
lastCheck time.Time
|
||||
}
|
||||
|
||||
// NewNativeShield creates the FFI bridge to the C++ shield engine.
|
||||
func NewNativeShield() (*NativeShield, error) {
|
||||
n := &NativeShield{
|
||||
version: "0.1.0-ffi",
|
||||
}
|
||||
|
||||
// TODO: Call C.shield_init()
|
||||
n.initialized = true
|
||||
n.lastCheck = time.Now()
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Inspect runs deep packet inspection on the payload.
|
||||
func (n *NativeShield) Inspect(payload []byte) ShieldResult {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if !n.initialized {
|
||||
return ShieldResult{Error: "engine not initialized"}
|
||||
}
|
||||
|
||||
// TODO: FFI call
|
||||
// cPayload := C.CBytes(payload)
|
||||
// defer C.free(cPayload)
|
||||
// result := C.shield_inspect((*C.char)(cPayload), C.int(len(payload)))
|
||||
|
||||
return ShieldResult{
|
||||
Blocked: false,
|
||||
Reason: "",
|
||||
Confidence: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
// Status returns the engine health via FFI.
|
||||
func (n *NativeShield) Status() EngineStatus {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
if !n.initialized {
|
||||
return EngineOffline
|
||||
}
|
||||
|
||||
return EngineHealthy
|
||||
}
|
||||
|
||||
// Name returns the engine identifier.
|
||||
func (n *NativeShield) Name() string {
|
||||
return "shield"
|
||||
}
|
||||
|
||||
// Version returns the native library version.
|
||||
func (n *NativeShield) Version() string {
|
||||
return n.version
|
||||
}
|
||||
|
||||
// Shutdown gracefully closes the FFI bridge.
|
||||
func (n *NativeShield) Shutdown() error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
// TODO: C.shield_shutdown()
|
||||
n.initialized = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShieldResult is returned by the Inspect function.
|
||||
type ShieldResult struct {
|
||||
Blocked bool `json:"blocked"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue