Release prep: 54 engines, self-hosted signatures, i18n, dashboard updates

This commit is contained in:
DmitrL-dev 2026-03-23 16:45:40 +10:00
parent 694e32be26
commit 41cbfd6e0a
178 changed files with 36008 additions and 399 deletions

View 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
}

View 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)

View 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"`
}

View 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"`
}