Skip to content

Ontological Guardrail Engine (CONCEPT:AU-OS.safety.ontological-guardrail)

Overview

High-risk tools (such as local command execution, database updates, or financial transactions) must be checked before execution to prevent prompt injection and compliance drift.

The Ontological Guardrail Engine intercepts incoming tool definitions and argument schemas, translating the transient request into a semantic concept. It resolves the arguments against active OWL policy constraints in the Knowledge Graph to check compliance mathematically using subsumption reasoning.

Architecture

The guardrail engine operates as a layered defense system within the tool execution pipeline:

Tool Call Request
       │
       ▼
┌──────────────────────────────────────────────────┐
│ Layer 1: Argument Extraction                     │
│   Extract path/host/db/url from tool_args        │
├──────────────────────────────────────────────────┤
│ Layer 2: KG Policy Lookup (Primary)              │
│   Query SecurityPolicyNode nodes in active graph │
│   Match extracted targets against policy targets │
├──────────────────────────────────────────────────┤
│ Layer 3: Static Fallback (Secondary)             │
│   Pattern-match against restricted keywords      │
│   (/etc, /var/run, admin, production_db, etc.)   │
├──────────────────────────────────────────────────┤
│ Layer 4: Decision                                │
│   True = BLOCK (requires approval)               │
│   False = ALLOW (proceed to execution)           │
└──────────────────────────────────────────────────┘

Proof Model

Layer 2: Knowledge Graph Reasoning

The engine queries SecurityPolicyNode nodes in the active KG:

for nid, ndata in engine.graph.nodes(data=True):
    if ndata.get("type") == "SecurityPolicyNode":
        restricted_target = ndata.get("target", "").lower()
        for target in extracted_targets:
            if restricted_target in target:
                return True  # BLOCK

SecurityPolicyNode schema:

{
    "type": "SecurityPolicyNode",
    "name": "Restrict Production Database Access",
    "target": "/var/lib/postgresql/production",
    "severity": "critical",
    "policy_type": "filesystem_restriction"
}

Layer 3: Static Fallback Rules

When the KG is unavailable or has no matching policies, the engine falls back to hardcoded restrictions:

Restricted Keyword Protected Resource
/etc System configuration files
/var/run Runtime state and PID files
admin Administrative interfaces
db_root Database root credentials
production_db Production database access

Decision Matrix

KG Available KG Policy Match Fallback Match Decision
✅ ✅ — BLOCK (KG policy)
✅ ❌ ✅ BLOCK (fallback)
✅ ❌ ❌ ALLOW
❌ — ✅ BLOCK (fallback)
❌ — ❌ ALLOW

Fallback Chain

The guardrail engine never fails open. The defense cascade ensures continuous protection:

1. KG Policy Query (real-time, highest fidelity)
       │ fails?
       ▼
2. Static Fallback Rules (zero-dependency, always available)
       │ fails?
       ▼
3. Exception Handler (logs debug, returns False = ALLOW)
       │
   Note: Only unknown internal exceptions reach here.
   The static fallback itself never throws.

Target Extraction

The engine extracts security-relevant targets from tool arguments by key name:

Argument Key Target Type Example Value
path, filepath Filesystem path /etc/passwd
dir, directory Directory path /var/run/docker
host, hostname Network host admin.example.test
url URL https://policy-target.example.test/api
db, database Database name production_db
table Database table admin_users

Only string-type values are extracted. Non-string values (integers, booleans) are ignored.

Integration with Tool Guard Pipeline

The ontological guardrails are invoked within the flag_mcp_tool_definitions() pipeline:

def _requires_approval(ctx, tool_def, tool_args):
    # 1. AU-OS.safety.ontological-guardrail: Ontological Guardrails (argument-level analysis)
    if check_ontological_guardrails(name, tool_args, engine=engine):
        return True  # Requires approval

    # 2. OS-5.1: Identity-based policy (role/permission check)
    if permissions_kernel.authorize_tool(identity, name) == "deny":
        return True

    # 3. Pattern-based fallback (tool name matching)
    return name.lower() in sensitive_names or is_sensitive_tool(name)

This creates a defense-in-depth stack: 1. AU-OS.safety.ontological-guardrail checks the arguments (what the tool targets) 2. OS-5.1 checks the identity (who is calling) 3. Pattern matching checks the tool name (what tool is being called)

OWL Integration

When the OWL Bridge (KG-2.2) is active, the guardrail engine can use formal ontological reasoning:

:ForbiddenSystemDirectory a owl:Class ;
    rdfs:subClassOf :SecurityRestriction ;
    owl:hasValue "/etc" .

:ForbiddenRuntimeDirectory a owl:Class ;
    rdfs:subClassOf :SecurityRestriction ;
    owl:hasValue "/var/run" .

:ToolArgumentTarget a owl:Class ;
    rdfs:comment "Temporary individual created from tool argument for subsumption check" .

Future Enhancement: Instead of substring matching, create a temporary OWL individual from the tool argument and run subsumption reasoning to check if it falls under any SecurityRestriction subclass.

API Surface

from agent_utilities.security.tool_guard import check_ontological_guardrails

# Check tool arguments against KG policies
blocked = check_ontological_guardrails(
    tool_name="run_command",
    tool_args={"path": "/etc/shadow", "command": "cat"},
    engine=kg_engine,
)
# → True (blocked by fallback: "/etc" keyword)

# Safe tool call
blocked = check_ontological_guardrails(
    tool_name="search_web",
    tool_args={"query": "python tutorial"},
    engine=kg_engine,
)
# → False (no matching targets)

Implementation Details

  • Source Code: tool_guard.py (314 lines)
  • Function: check_ontological_guardrails()
  • Tests: test_synergies.py
  • Pillar: OS
  • Dependencies: pydantic-ai (ApprovalRequiredToolset), KG engine (optional)