mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-12 19:52:27 +02:00
265 lines
9.3 KiB
Python
265 lines
9.3 KiB
Python
import ast
|
|
from pathlib import Path
|
|
|
|
|
|
BACKEND_DIR = Path(__file__).resolve().parents[2]
|
|
HANDLER_MODULES = [
|
|
BACKEND_DIR / "main.py",
|
|
BACKEND_DIR / "routers" / "mesh_dm.py",
|
|
BACKEND_DIR / "routers" / "mesh_public.py",
|
|
BACKEND_DIR / "routers" / "mesh_oracle.py",
|
|
]
|
|
WRITE_METHODS = {"post", "put", "patch", "delete"}
|
|
ALLOWED_EXEMPTIONS = {"PEER_GOSSIP", "ADMIN_CONTROL", "LOCAL_OPERATOR_ONLY"}
|
|
FORBIDDEN_ROUTE_HELPERS = {
|
|
"_verify_signed_event",
|
|
"_preflight_signed_event_integrity",
|
|
"verify_signed_event",
|
|
"preflight_signed_event_integrity",
|
|
"_verify_signed_write",
|
|
"verify_signed_write",
|
|
"_verify_gate_message_signed_write",
|
|
"verify_gate_message_signed_write",
|
|
}
|
|
|
|
|
|
def _verify_signature_call_lines(path: Path) -> list[int]:
|
|
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
|
|
lines: list[int] = []
|
|
for node in ast.walk(tree):
|
|
if not isinstance(node, ast.Call):
|
|
continue
|
|
if isinstance(node.func, ast.Name) and node.func.id == "verify_signature":
|
|
lines.append(node.lineno)
|
|
elif isinstance(node.func, ast.Attribute) and node.func.attr == "verify_signature":
|
|
lines.append(node.lineno)
|
|
return sorted(lines)
|
|
|
|
|
|
def _route_decorators(node: ast.AST) -> list[ast.Call]:
|
|
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
return []
|
|
return [
|
|
decorator
|
|
for decorator in node.decorator_list
|
|
if isinstance(decorator, ast.Call)
|
|
and isinstance(decorator.func, ast.Attribute)
|
|
and decorator.func.attr in {"get", "post", "put", "patch", "delete"}
|
|
]
|
|
|
|
|
|
def _mesh_write_route(node: ast.AST) -> bool:
|
|
for decorator in _route_decorators(node):
|
|
if decorator.func.attr not in WRITE_METHODS or not decorator.args:
|
|
continue
|
|
first_arg = decorator.args[0]
|
|
if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str):
|
|
if first_arg.value.startswith("/api/mesh/"):
|
|
return True
|
|
return False
|
|
|
|
|
|
def _has_named_decorator(node: ast.AST, name: str) -> bool:
|
|
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
return False
|
|
for decorator in node.decorator_list:
|
|
if isinstance(decorator, ast.Call):
|
|
func = decorator.func
|
|
if isinstance(func, ast.Name) and func.id == name:
|
|
return True
|
|
if isinstance(func, ast.Attribute) and func.attr == name:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _exemption_value(node: ast.AST) -> str | None:
|
|
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
return None
|
|
for decorator in node.decorator_list:
|
|
if not isinstance(decorator, ast.Call):
|
|
continue
|
|
func = decorator.func
|
|
func_name = func.id if isinstance(func, ast.Name) else getattr(func, "attr", "")
|
|
if func_name != "mesh_write_exempt" or len(decorator.args) != 1:
|
|
continue
|
|
arg = decorator.args[0]
|
|
if isinstance(arg, ast.Attribute) and isinstance(arg.value, ast.Name) and arg.value.id == "MeshWriteExemption":
|
|
return arg.attr
|
|
return "__invalid__"
|
|
return None
|
|
|
|
|
|
def _request_json_call_lines(node: ast.AST) -> list[int]:
|
|
lines: list[int] = []
|
|
for child in ast.walk(node):
|
|
if not isinstance(child, ast.Call):
|
|
continue
|
|
if isinstance(child.func, ast.Attribute) and child.func.attr == "json":
|
|
owner = child.func.value
|
|
if isinstance(owner, ast.Name) and owner.id == "request":
|
|
lines.append(child.lineno)
|
|
return sorted(lines)
|
|
|
|
|
|
def _forbidden_route_helper_lines(node: ast.AST) -> list[int]:
|
|
lines: list[int] = []
|
|
for child in ast.walk(node):
|
|
if not isinstance(child, ast.Call):
|
|
continue
|
|
if isinstance(child.func, ast.Name) and child.func.id in FORBIDDEN_ROUTE_HELPERS:
|
|
lines.append(child.lineno)
|
|
elif isinstance(child.func, ast.Attribute) and child.func.attr in FORBIDDEN_ROUTE_HELPERS:
|
|
lines.append(child.lineno)
|
|
return sorted(lines)
|
|
|
|
|
|
def _mesh_write_contract_report(path: Path) -> dict[str, dict[str, object]]:
|
|
tree = ast.parse(path.read_text(encoding="utf-8-sig"))
|
|
report: dict[str, dict[str, object]] = {}
|
|
for node in tree.body:
|
|
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
continue
|
|
if not _mesh_write_route(node):
|
|
continue
|
|
exemption = _exemption_value(node)
|
|
decorated = _has_named_decorator(node, "requires_signed_write")
|
|
report[node.name] = {
|
|
"decorated": decorated,
|
|
"exemption": exemption,
|
|
"json_lines": _request_json_call_lines(node),
|
|
"forbidden_lines": _forbidden_route_helper_lines(node),
|
|
}
|
|
return report
|
|
|
|
|
|
def test_request_handlers_do_not_call_verify_signature_directly():
|
|
offenders = {
|
|
str(path.relative_to(BACKEND_DIR)): _verify_signature_call_lines(path)
|
|
for path in HANDLER_MODULES
|
|
if _verify_signature_call_lines(path)
|
|
}
|
|
assert not offenders, (
|
|
"Request-handling modules must route signature checks through "
|
|
"services.mesh.mesh_signed_events, not raw verify_signature(). "
|
|
f"Found direct calls: {offenders}"
|
|
)
|
|
|
|
|
|
def test_mesh_write_handlers_are_decorated_or_explicitly_exempt():
|
|
offenders: dict[str, dict[str, dict[str, object]]] = {}
|
|
for path in HANDLER_MODULES:
|
|
report = _mesh_write_contract_report(path)
|
|
missing = {
|
|
name: details
|
|
for name, details in report.items()
|
|
if not details["decorated"] and details["exemption"] is None
|
|
}
|
|
if missing:
|
|
offenders[str(path.relative_to(BACKEND_DIR))] = missing
|
|
assert not offenders, (
|
|
"Every /api/mesh write handler must use @requires_signed_write(...) or "
|
|
"@mesh_write_exempt(MeshWriteExemption.*). "
|
|
f"Missing coverage: {offenders}"
|
|
)
|
|
|
|
|
|
def test_mesh_write_exemptions_use_fixed_enum_reasons():
|
|
offenders: dict[str, dict[str, str]] = {}
|
|
for path in HANDLER_MODULES:
|
|
report = _mesh_write_contract_report(path)
|
|
invalid = {}
|
|
for name, details in report.items():
|
|
exemption = details["exemption"]
|
|
if exemption is None:
|
|
continue
|
|
if exemption not in ALLOWED_EXEMPTIONS:
|
|
invalid[name] = str(exemption)
|
|
if invalid:
|
|
offenders[str(path.relative_to(BACKEND_DIR))] = invalid
|
|
assert not offenders, (
|
|
"mesh_write_exempt must use the fixed MeshWriteExemption enum values only. "
|
|
f"Invalid exemptions: {offenders}"
|
|
)
|
|
|
|
|
|
def test_decorated_mesh_write_handlers_do_not_reparse_request_json():
|
|
offenders: dict[str, dict[str, list[int]]] = {}
|
|
for path in HANDLER_MODULES:
|
|
report = _mesh_write_contract_report(path)
|
|
invalid = {
|
|
name: details["json_lines"]
|
|
for name, details in report.items()
|
|
if details["decorated"] and details["json_lines"]
|
|
}
|
|
if invalid:
|
|
offenders[str(path.relative_to(BACKEND_DIR))] = invalid
|
|
assert not offenders, (
|
|
"Decorated /api/mesh write handlers must not call request.json(); they must "
|
|
"consume the shared signed-write body cache. "
|
|
f"Found reparses: {offenders}"
|
|
)
|
|
|
|
|
|
def test_mesh_write_route_handlers_do_not_inline_signed_verification():
|
|
offenders: dict[str, dict[str, list[int]]] = {}
|
|
for path in HANDLER_MODULES:
|
|
report = _mesh_write_contract_report(path)
|
|
invalid = {
|
|
name: details["forbidden_lines"]
|
|
for name, details in report.items()
|
|
if details["forbidden_lines"]
|
|
}
|
|
if invalid:
|
|
offenders[str(path.relative_to(BACKEND_DIR))] = invalid
|
|
assert not offenders, (
|
|
"Route-decorated /api/mesh write handlers must not inline signed-write "
|
|
"verification helpers. The decorator is the enforcement point. "
|
|
f"Found route bypasses: {offenders}"
|
|
)
|
|
|
|
|
|
def test_ast_guard_self_test_rejects_unmarked_mesh_write(tmp_path):
|
|
path = tmp_path / "fake_mesh_routes.py"
|
|
path.write_text(
|
|
"""
|
|
from fastapi import APIRouter, Request
|
|
|
|
router = APIRouter()
|
|
|
|
@router.post("/api/mesh/fake")
|
|
async def fake_write(request: Request):
|
|
return {"ok": True}
|
|
""".strip(),
|
|
encoding="utf-8",
|
|
)
|
|
report = _mesh_write_contract_report(path)
|
|
assert report["fake_write"]["decorated"] is False
|
|
assert report["fake_write"]["exemption"] is None
|
|
|
|
|
|
def test_ast_guard_self_test_rejects_free_text_exemption_and_json_reparse(tmp_path):
|
|
path = tmp_path / "fake_mesh_routes.py"
|
|
path.write_text(
|
|
"""
|
|
from fastapi import APIRouter, Request
|
|
from services.mesh.mesh_signed_events import mesh_write_exempt, requires_signed_write
|
|
|
|
router = APIRouter()
|
|
|
|
@router.post("/api/mesh/fake-exempt")
|
|
@mesh_write_exempt("free_text")
|
|
async def fake_exempt(request: Request):
|
|
return {"ok": True}
|
|
|
|
@router.post("/api/mesh/fake-decorated")
|
|
@requires_signed_write(kind="mesh_send")
|
|
async def fake_decorated(request: Request):
|
|
body = await request.json()
|
|
return {"ok": bool(body)}
|
|
""".strip(),
|
|
encoding="utf-8",
|
|
)
|
|
report = _mesh_write_contract_report(path)
|
|
assert report["fake_exempt"]["exemption"] == "__invalid__"
|
|
assert len(report["fake_decorated"]["json_lines"]) == 1
|