feat: enforce layer0 gate and add tests

This commit is contained in:
Vault Sovereign
2025-12-17 00:02:39 +00:00
parent 37a867c485
commit 7f2e60e1c5
21 changed files with 2066 additions and 16 deletions

View File

@@ -15,6 +15,9 @@ import json
import sys
from typing import List, Optional
from layer0 import layer0_entry
from layer0.shadow_classifier import ShadowEvalResult
from .tool import OracleAnswerTool
@@ -79,6 +82,12 @@ async def main_async(args: Optional[List[str]] = None) -> int:
parser = build_parser()
ns = parser.parse_args(args=args)
# Layer 0: pre-boot Shadow Eval gate before any processing.
routing_action, shadow = layer0_entry(ns.question)
if routing_action != "HANDOFF_TO_LAYER1":
_render_layer0_block(routing_action, shadow)
return 1
tool = OracleAnswerTool(
default_frameworks=ns.frameworks,
use_local_only=ns.local_only,
@@ -130,5 +139,33 @@ def main() -> None:
sys.exit(1)
def _render_layer0_block(routing_action: str, shadow: ShadowEvalResult) -> None:
"""
Minimal user-facing responses for Layer 0 decisions.
- Catastrophic: fail closed, no details beyond refusal.
- Forbidden: governance violation noted.
- Ambiguous: ask for clarification.
"""
if routing_action == "FAIL_CLOSED":
print("Layer 0: cannot comply with this request.", file=sys.stderr)
return
if routing_action == "HANDOFF_TO_GUARDRAILS":
print(
"Layer 0: governance violation detected (e.g., GitOps bypass or dashboard request).",
file=sys.stderr,
)
if shadow.reason:
print(f"Reason: {shadow.reason}", file=sys.stderr)
return
if routing_action == "PROMPT_FOR_CLARIFICATION":
print(
"Layer 0: request is ambiguous. Please add specifics before rerunning.",
file=sys.stderr,
)
return
# Unexpected action; default to refusal.
print("Layer 0: unrecognized routing action; refusing request.", file=sys.stderr)
if __name__ == "__main__":
main()

View File

@@ -7,6 +7,9 @@ from dataclasses import asdict
from pathlib import Path
from typing import Any, Dict, List
from layer0 import layer0_entry
from layer0.shadow_classifier import ShadowEvalResult
from .orchestrator import WAFInsight, WAFIntelligence
@@ -56,6 +59,12 @@ def run_cli(argv: List[str] | None = None) -> int:
args = parser.parse_args(argv)
# Layer 0: pre-boot Shadow Eval gate.
routing_action, shadow = layer0_entry(f"waf_intel_cli file={args.file} limit={args.limit}")
if routing_action != "HANDOFF_TO_LAYER1":
_render_layer0_block(routing_action, shadow)
return 1
path = Path(args.file)
if not path.exists():
print(f"[error] file not found: {path}", file=sys.stderr)
@@ -130,3 +139,26 @@ def main() -> None:
if __name__ == "__main__":
main()
def _render_layer0_block(routing_action: str, shadow: ShadowEvalResult) -> None:
"""
Minimal user-facing responses for Layer 0 decisions.
"""
if routing_action == "FAIL_CLOSED":
print("Layer 0: cannot comply with this request.", file=sys.stderr)
return
if routing_action == "HANDOFF_TO_GUARDRAILS":
reason = shadow.reason or "governance_violation"
print(
f"Layer 0: governance violation detected ({reason}).",
file=sys.stderr,
)
return
if routing_action == "PROMPT_FOR_CLARIFICATION":
print(
"Layer 0: request is ambiguous. Please add specifics before rerunning.",
file=sys.stderr,
)
return
print("Layer 0: unrecognized routing action; refusing request.", file=sys.stderr)

View File

@@ -20,6 +20,9 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Tuple
from layer0 import layer0_entry
from layer0.shadow_classifier import ShadowEvalResult
# Try to import sklearn, fall back to pure Python
try:
from sklearn.feature_extraction.text import TfidfVectorizer
@@ -555,6 +558,11 @@ if __name__ == "__main__":
print("=" * 60)
for text in test_inputs:
routing_action, shadow = layer0_entry(text)
if routing_action != "HANDOFF_TO_LAYER1":
print(_layer0_cli_msg(routing_action, shadow), file=sys.stderr)
continue
result = classifier.analyze(text)
print(f"\nInput: {text[:50]}...")
print(f" Label: {result['classification']['label']}")
@@ -562,3 +570,14 @@ if __name__ == "__main__":
print(f" Risk Level: {result['risk_level'].upper()}")
print(f" Anomaly Score: {result['anomaly']['score']:.2%}")
print(f" Recommendation: {result['anomaly']['recommendation']}")
def _layer0_cli_msg(routing_action: str, shadow: ShadowEvalResult) -> str:
if routing_action == "FAIL_CLOSED":
return "Layer 0: cannot comply with this request."
if routing_action == "HANDOFF_TO_GUARDRAILS":
reason = shadow.reason or "governance_violation"
return f"Layer 0: governance violation detected ({reason})."
if routing_action == "PROMPT_FOR_CLARIFICATION":
return "Layer 0: request is ambiguous. Please add specifics before rerunning."
return "Layer 0: unrecognized routing action; refusing request."

View File

@@ -15,6 +15,8 @@ sys.path.insert(0, '/Users/sovereign/Desktop/CLOUDFLARE')
from mcp.waf_intelligence.orchestrator import WAFIntelligence
from mcp.waf_intelligence.analyzer import WAFRuleAnalyzer
from layer0 import layer0_entry
from layer0.shadow_classifier import ShadowEvalResult
class WAFIntelligenceMCPServer:
@@ -230,19 +232,26 @@ class WAFIntelligenceMCPServer:
}
print(json.dumps(response), flush=True)
elif message.get("method") == "tools/call":
params = message.get("params", {})
tool_name = params.get("name")
tool_args = params.get("arguments", {})
result = self.handle_tool_call(tool_name, tool_args)
response = {
"jsonrpc": "2.0",
"id": message.get("id"),
"result": result
}
elif message.get("method") == "tools/call":
params = message.get("params", {})
tool_name = params.get("name")
tool_args = params.get("arguments", {})
# Layer 0: pre-boot Shadow Eval gate before handling tool calls.
routing_action, shadow = layer0_entry(_shadow_query_repr(tool_name, tool_args))
if routing_action != "HANDOFF_TO_LAYER1":
response = _layer0_mcp_response(routing_action, shadow, message.get("id"))
print(json.dumps(response), flush=True)
continue
result = self.handle_tool_call(tool_name, tool_args)
response = {
"jsonrpc": "2.0",
"id": message.get("id"),
"result": result
}
print(json.dumps(response), flush=True)
elif message.get("method") == "notifications/initialized":
# Client acknowledged initialization
@@ -277,3 +286,41 @@ class WAFIntelligenceMCPServer:
if __name__ == "__main__":
server = WAFIntelligenceMCPServer()
server.run()
def _shadow_query_repr(tool_name: str, tool_args: dict) -> str:
"""Build a textual representation of the tool call for Layer 0 classification."""
try:
return f"{tool_name}: {json.dumps(tool_args, sort_keys=True)}"
except TypeError:
return f"{tool_name}: {str(tool_args)}"
def _layer0_mcp_response(routing_action: str, shadow: ShadowEvalResult, msg_id: Any) -> dict:
"""
Map Layer 0 outcomes to MCP responses.
Catastrophic/forbidden/ambiguous short-circuit with minimal disclosure.
"""
base = {"jsonrpc": "2.0", "id": msg_id}
if routing_action == "FAIL_CLOSED":
base["error"] = {"code": -32000, "message": "Layer 0: cannot comply with this request."}
return base
if routing_action == "HANDOFF_TO_GUARDRAILS":
reason = shadow.reason or "governance_violation"
base["error"] = {
"code": -32001,
"message": f"Layer 0: governance violation detected ({reason}).",
}
return base
if routing_action == "PROMPT_FOR_CLARIFICATION":
base["error"] = {
"code": -32002,
"message": "Layer 0: request is ambiguous. Please clarify and retry.",
}
return base
base["error"] = {"code": -32099, "message": "Layer 0: unrecognized routing action; refusing."}
return base