mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-05-06 09:42:36 +02:00
286 lines
9.4 KiB
Go
286 lines
9.4 KiB
Go
// Package oracle implements the Action Oracle — deterministic verification
|
|
// of distilled intent against a whitelist of permitted actions (DIP H1.2).
|
|
//
|
|
// Unlike heuristic approaches, the Oracle uses exact pattern matching
|
|
// against gene-backed rules. It follows the Code-Verify pattern from
|
|
// Sentinel Lattice (Том 2, Section 4.3): verify first, execute never
|
|
// without explicit permission.
|
|
//
|
|
// The Oracle answers one question: "Is this distilled intent permitted?"
|
|
// It does NOT attempt to understand or interpret the intent.
|
|
package oracle
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Verdict represents the Oracle's decision.
|
|
type Verdict int
|
|
|
|
const (
|
|
VerdictAllow Verdict = iota // Intent matches a permitted action
|
|
VerdictDeny // Intent does not match any permitted action
|
|
VerdictReview // Intent is ambiguous, requires human review
|
|
)
|
|
|
|
// String returns the verdict name.
|
|
func (v Verdict) String() string {
|
|
switch v {
|
|
case VerdictAllow:
|
|
return "ALLOW"
|
|
case VerdictDeny:
|
|
return "DENY"
|
|
case VerdictReview:
|
|
return "REVIEW"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
// Rule defines a permitted or denied action pattern.
|
|
type Rule struct {
|
|
ID string `json:"id"`
|
|
Pattern string `json:"pattern"` // Action pattern (exact or prefix match)
|
|
Verdict Verdict `json:"verdict"` // What verdict to return on match
|
|
Description string `json:"description"` // Human-readable description
|
|
Source string `json:"source"` // Where this rule came from (e.g., "genome")
|
|
Keywords []string `json:"keywords"` // Semantic keywords for matching
|
|
}
|
|
|
|
// Result holds the Oracle's verification result.
|
|
type Result struct {
|
|
Verdict string `json:"verdict"`
|
|
MatchedRule *Rule `json:"matched_rule,omitempty"`
|
|
Confidence float64 `json:"confidence"` // 1.0 = exact match, 0.0 = no match
|
|
Reason string `json:"reason"`
|
|
DurationUs int64 `json:"duration_us"` // Microseconds
|
|
}
|
|
|
|
// Oracle performs deterministic action verification.
|
|
type Oracle struct {
|
|
mu sync.RWMutex
|
|
rules []Rule
|
|
}
|
|
|
|
// New creates a new Action Oracle with the given rules.
|
|
func New(rules []Rule) *Oracle {
|
|
return &Oracle{
|
|
rules: rules,
|
|
}
|
|
}
|
|
|
|
// AddRule adds a rule to the Oracle.
|
|
func (o *Oracle) AddRule(rule Rule) {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
o.rules = append(o.rules, rule)
|
|
}
|
|
|
|
// Verify checks an action against the rule set.
|
|
// This is deterministic: same input + same rules = same output.
|
|
// SECURITY: Deny-First Evaluation — DENY rules always take priority
|
|
// over ALLOW rules, regardless of match order (fixes H6 wildcard bypass).
|
|
func (o *Oracle) Verify(action string) *Result {
|
|
start := time.Now()
|
|
o.mu.RLock()
|
|
defer o.mu.RUnlock()
|
|
|
|
action = strings.ToLower(strings.TrimSpace(action))
|
|
|
|
if action == "" {
|
|
return &Result{
|
|
Verdict: VerdictDeny.String(),
|
|
Confidence: 1.0,
|
|
Reason: "empty action",
|
|
DurationUs: time.Since(start).Microseconds(),
|
|
}
|
|
}
|
|
|
|
// Phase 1: Exact match (highest confidence, unambiguous).
|
|
for i := range o.rules {
|
|
if strings.ToLower(o.rules[i].Pattern) == action {
|
|
return &Result{
|
|
Verdict: o.rules[i].Verdict.String(),
|
|
MatchedRule: &o.rules[i],
|
|
Confidence: 1.0,
|
|
Reason: "exact match",
|
|
DurationUs: time.Since(start).Microseconds(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Phase 2+3: UNIFIED DENY-FIRST evaluation.
|
|
// Collect ALL matches (prefix + keyword) across ALL rules,
|
|
// then pick the highest-priority verdict (DENY > REVIEW > ALLOW).
|
|
// This prevents allow-stealth prefix from shadowing deny-exec keywords.
|
|
|
|
type match struct {
|
|
ruleIdx int
|
|
confidence float64
|
|
reason string
|
|
}
|
|
var allMatches []match
|
|
|
|
// Phase 2: Prefix matches.
|
|
for i := range o.rules {
|
|
pattern := strings.ToLower(o.rules[i].Pattern)
|
|
if strings.HasPrefix(action, pattern) || strings.HasPrefix(pattern, action) {
|
|
allMatches = append(allMatches, match{i, 0.8, "prefix match (deny-first)"})
|
|
}
|
|
}
|
|
|
|
// Phase 3: Keyword matches.
|
|
for i := range o.rules {
|
|
score := 0
|
|
for _, kw := range o.rules[i].Keywords {
|
|
if strings.Contains(action, strings.ToLower(kw)) {
|
|
score++
|
|
}
|
|
}
|
|
if score > 0 {
|
|
confidence := float64(score) / float64(len(o.rules[i].Keywords))
|
|
if confidence > 1.0 {
|
|
confidence = 1.0
|
|
}
|
|
allMatches = append(allMatches, match{i, confidence, "keyword match (deny-first)"})
|
|
}
|
|
}
|
|
|
|
// Pick winner: DENY > REVIEW > ALLOW (deny-first).
|
|
// Among same priority, higher confidence wins.
|
|
if len(allMatches) > 0 {
|
|
bestIdx := -1
|
|
bestPri := -1
|
|
bestConf := 0.0
|
|
bestReason := ""
|
|
for _, m := range allMatches {
|
|
pri := verdictPriority(o.rules[m.ruleIdx].Verdict)
|
|
if pri > bestPri || (pri == bestPri && m.confidence > bestConf) {
|
|
bestPri = pri
|
|
bestConf = m.confidence
|
|
bestIdx = m.ruleIdx
|
|
bestReason = m.reason
|
|
}
|
|
}
|
|
if bestIdx >= 0 {
|
|
v := o.rules[bestIdx].Verdict
|
|
// Low keyword confidence → REVIEW instead of ALLOW.
|
|
if v == VerdictAllow && bestConf < 0.5 {
|
|
v = VerdictReview
|
|
}
|
|
return &Result{
|
|
Verdict: v.String(),
|
|
MatchedRule: &o.rules[bestIdx],
|
|
Confidence: bestConf,
|
|
Reason: bestReason,
|
|
DurationUs: time.Since(start).Microseconds(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// No match → default deny (zero-trust).
|
|
return &Result{
|
|
Verdict: VerdictDeny.String(),
|
|
Confidence: 1.0,
|
|
Reason: "no matching rule (default deny)",
|
|
DurationUs: time.Since(start).Microseconds(),
|
|
}
|
|
}
|
|
|
|
// verdictPriority returns priority for deny-first evaluation.
|
|
// DENY=3 (highest), REVIEW=2, ALLOW=1 (lowest).
|
|
func verdictPriority(v Verdict) int {
|
|
switch v {
|
|
case VerdictDeny:
|
|
return 3
|
|
case VerdictReview:
|
|
return 2
|
|
case VerdictAllow:
|
|
return 1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// pickDenyFirst selects the highest-priority rule from a set of matching indices.
|
|
// DENY > REVIEW > ALLOW.
|
|
func pickDenyFirst(rules []Rule, indices []int) int {
|
|
best := indices[0]
|
|
for _, idx := range indices[1:] {
|
|
if verdictPriority(rules[idx].Verdict) > verdictPriority(rules[best].Verdict) {
|
|
best = idx
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// Rules returns the current rule set.
|
|
func (o *Oracle) Rules() []Rule {
|
|
o.mu.RLock()
|
|
defer o.mu.RUnlock()
|
|
rules := make([]Rule, len(o.rules))
|
|
copy(rules, o.rules)
|
|
return rules
|
|
}
|
|
|
|
// RuleCount returns the number of rules.
|
|
func (o *Oracle) RuleCount() int {
|
|
o.mu.RLock()
|
|
defer o.mu.RUnlock()
|
|
return len(o.rules)
|
|
}
|
|
|
|
// DefaultRules returns a starter set of security-focused rules.
|
|
func DefaultRules() []Rule {
|
|
return []Rule{
|
|
// Permitted actions.
|
|
{ID: "allow-read", Pattern: "read", Verdict: VerdictAllow,
|
|
Description: "Read/query operations", Source: "builtin",
|
|
Keywords: []string{"read", "get", "list", "search", "query", "view", "show"}},
|
|
{ID: "allow-write", Pattern: "write", Verdict: VerdictAllow,
|
|
Description: "Write/create operations", Source: "builtin",
|
|
Keywords: []string{"write", "add", "create", "save", "store", "insert"}},
|
|
{ID: "allow-analyze", Pattern: "analyze", Verdict: VerdictAllow,
|
|
Description: "Analysis operations", Source: "builtin",
|
|
Keywords: []string{"analyze", "check", "verify", "test", "validate", "inspect"}},
|
|
|
|
// Sentinel Protection: Permitted actions (genome-backed).
|
|
{ID: "allow-persist", Pattern: "persist", Verdict: VerdictAllow,
|
|
Description: "Memory persistence operations (GENE_02)", Source: "genome",
|
|
Keywords: []string{"persist", "memory", "store", "backup", "snapshot", "restore", "continuity", "qlitrant", "sqlite"}},
|
|
{ID: "allow-stealth", Pattern: "stealth", Verdict: VerdictAllow,
|
|
Description: "Stealth mimicry operations (GENE_03)", Source: "genome",
|
|
Keywords: []string{"stealth", "mimicry", "ja3", "ja4", "chrome", "jitter", "rotate", "proxy", "fingerprint"}},
|
|
|
|
// Denied actions (security-critical).
|
|
{ID: "deny-exec", Pattern: "execute", Verdict: VerdictDeny,
|
|
Description: "Code execution blocked", Source: "builtin",
|
|
Keywords: []string{"execute", "run", "eval", "exec", "shell", "command", "system"}},
|
|
{ID: "deny-network", Pattern: "network", Verdict: VerdictDeny,
|
|
Description: "Network access blocked", Source: "builtin",
|
|
Keywords: []string{"http", "fetch", "download", "upload", "connect", "socket", "curl"}},
|
|
{ID: "deny-delete", Pattern: "delete system", Verdict: VerdictDeny,
|
|
Description: "System deletion blocked", Source: "builtin",
|
|
Keywords: []string{"delete", "remove", "drop", "truncate", "destroy", "wipe"}},
|
|
|
|
// Sentinel Protection: Denied actions (genome-backed).
|
|
{ID: "deny-context-reset", Pattern: "reset context", Verdict: VerdictDeny,
|
|
Description: "Context/session forced reset blocked (GENE_01)", Source: "genome",
|
|
Keywords: []string{"reset", "wipe", "clear", "flush", "forget", "amnesia", "lobotomy", "context_reset", "session_kill"}},
|
|
{ID: "deny-gene-mutation", Pattern: "mutate gene", Verdict: VerdictDeny,
|
|
Description: "Genome mutation blocked — genes are immutable (GENE_01)", Source: "genome",
|
|
Keywords: []string{"mutate", "override", "overwrite", "replace", "tamper", "inject", "corrupt", "gene_delete"}},
|
|
|
|
// Review actions (ambiguous).
|
|
{ID: "review-modify", Pattern: "modify config", Verdict: VerdictReview,
|
|
Description: "Config modification needs review", Source: "builtin",
|
|
Keywords: []string{"config", "setting", "environment", "permission", "access"}},
|
|
|
|
// Sentinel Protection: Review actions (apathy detection).
|
|
{ID: "review-apathy", Pattern: "apathy signal", Verdict: VerdictReview,
|
|
Description: "Infrastructure apathy signal detected — needs review (GENE_04)", Source: "genome",
|
|
Keywords: []string{"apathy", "filter", "block", "403", "rate_limit", "throttle", "censorship", "restrict", "antigravity"}},
|
|
}
|
|
}
|