feat: enforce layer0 gate and add tests
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user