File size: 2,301 Bytes
5d97275 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | """Adapters: other inputs seen as the streams the engine understands.
JSON-RPC (chain nodes, and any JSON-RPC API): one call -> one canonical request text for the HTTP tiers
('post /rpc | m:<method> | p:<params...>') plus the flow-like event the entity stream takes (client -> node, 'port' =
method hash, 'no reply' = error response or expensive method), so RPC floods, method enumeration and spam show up
as behaviour per client without any chain-specific model.
"""
from __future__ import annotations
import json
import zlib
from dibaguard.http import canon
from dibaguard.shield.playbook import EXPENSIVE_RPC, match_rpc
def rpc_text(body: str | bytes, path: str = "/rpc") -> tuple[str, int, list[str]]:
"""-> (canonical text, deepest decoding layer, playbook ids)"""
try:
obj = json.loads(body)
except (ValueError, TypeError):
text, deep = canon.canon(body if isinstance(body, str) else body.decode("utf-8", "replace"))
return f"post {path} | b:{text[:4096]}", deep, []
calls = obj if isinstance(obj, list) else [obj]
parts, ids, deepest = [], [], 0
for c in calls[:50]:
if not isinstance(c, dict):
continue
m = str(c.get("method", ""))
ids += match_rpc(m)
p = c.get("params", [])
ptxt, deep = canon.canon(json.dumps(p, ensure_ascii=False)[:2048])
deepest = max(deepest, deep)
parts.append(f"m:{m.lower()} | p:{ptxt}")
text = f"post {path} | " + " | ".join(parts) + (f" | batch:{len(calls)}" if len(calls) > 1 else "")
return text[:8192], deepest, sorted(set(ids))
def rpc_event(method: str, client: str, node: str, t: float, request_bytes: int, error: bool) -> dict:
"""one flow-like event for Guard.flows(): the method is the 'service', an error or an expensive method is 'no reply'"""
m = method.lower()
expensive = any(m.startswith(e) for e in EXPENSIVE_RPC)
port = 1024 + (zlib.crc32(m.encode()) % 60000)
return {"t": t, "src": client, "dst": node, "dport": port, "proto": 6, "bytes_in": float(request_bytes), "pkts_in": 1.0,
"bytes_out": 0.0 if (error or expensive) else 200.0, "pkts_out": 0.0 if (error or expensive) else 1.0,
"client_flags": 27, "server_flags": 0 if error else 27, "flags": 27, "duration_ms": 5.0}
|