mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-12 15:06:37 +02:00
1537 lines
52 KiB
Python
1537 lines
52 KiB
Python
import asyncio
|
|
import base64
|
|
import json
|
|
import logging
|
|
import time
|
|
from collections import deque
|
|
|
|
from cryptography.hazmat.primitives.asymmetric import ed25519
|
|
from starlette.requests import Request
|
|
|
|
import main
|
|
from services.mesh.mesh_crypto import build_signature_payload, derive_node_id
|
|
from services.mesh.mesh_hashchain import GateMessageStore
|
|
from services.mesh import mesh_reputation
|
|
|
|
|
|
def _json_request(path: str, body: dict) -> Request:
|
|
payload = json.dumps(body).encode("utf-8")
|
|
sent = {"value": False}
|
|
|
|
async def receive():
|
|
if sent["value"]:
|
|
return {"type": "http.request", "body": b"", "more_body": False}
|
|
sent["value"] = True
|
|
return {"type": "http.request", "body": payload, "more_body": False}
|
|
|
|
return Request(
|
|
{
|
|
"type": "http",
|
|
"headers": [(b"content-type", b"application/json")],
|
|
"client": ("test", 12345),
|
|
"method": "POST",
|
|
"path": path,
|
|
},
|
|
receive,
|
|
)
|
|
|
|
|
|
def _request(path: str, method: str = "GET") -> Request:
|
|
sent = {"value": False}
|
|
|
|
async def receive():
|
|
if sent["value"]:
|
|
return {"type": "http.request", "body": b"", "more_body": False}
|
|
sent["value"] = True
|
|
return {"type": "http.request", "body": b"", "more_body": False}
|
|
|
|
return Request(
|
|
{
|
|
"type": "http",
|
|
"headers": [],
|
|
"client": ("test", 12345),
|
|
"method": method,
|
|
"path": path,
|
|
},
|
|
receive,
|
|
)
|
|
|
|
|
|
def _signed_gate_event(
|
|
gate_id: str = "finance",
|
|
*,
|
|
private_key: ed25519.Ed25519PrivateKey | None = None,
|
|
sequence: int = 7,
|
|
ciphertext: str = "opaque-ciphertext",
|
|
nonce: str = "nonce-1",
|
|
sender_ref: str = "sender-ref-1",
|
|
) -> dict:
|
|
private_key = private_key or ed25519.Ed25519PrivateKey.generate()
|
|
public_key = base64.b64encode(private_key.public_key().public_bytes_raw()).decode("ascii")
|
|
node_id = derive_node_id(public_key)
|
|
payload = {
|
|
"gate": gate_id,
|
|
"ciphertext": ciphertext,
|
|
"nonce": nonce,
|
|
"sender_ref": sender_ref,
|
|
"format": "mls1",
|
|
}
|
|
signature = private_key.sign(
|
|
build_signature_payload(
|
|
event_type="gate_message",
|
|
node_id=node_id,
|
|
sequence=sequence,
|
|
payload=payload,
|
|
).encode("utf-8")
|
|
).hex()
|
|
return {
|
|
"event_type": "gate_message",
|
|
"timestamp": float(int(time.time() / 60) * 60),
|
|
"node_id": node_id,
|
|
"sequence": sequence,
|
|
"signature": signature,
|
|
"public_key": public_key,
|
|
"public_key_algo": "Ed25519",
|
|
"protocol_version": "infonet/2",
|
|
"payload": payload,
|
|
}
|
|
|
|
|
|
def _vote_event() -> dict:
|
|
return {
|
|
"event_id": "vote-1",
|
|
"event_type": "vote",
|
|
"node_id": "!node-1",
|
|
"payload": {"gate": "finance", "vote": 1},
|
|
"timestamp": 100.0,
|
|
"sequence": 3,
|
|
"signature": "sig",
|
|
"public_key": "pub",
|
|
"public_key_algo": "Ed25519",
|
|
"protocol_version": "infonet/2",
|
|
}
|
|
|
|
|
|
def _key_rotate_event() -> dict:
|
|
return {
|
|
"event_id": "rotate-1",
|
|
"event_type": "key_rotate",
|
|
"node_id": "!node-2",
|
|
"payload": {
|
|
"old_node_id": "!old-node",
|
|
"old_public_key": "old-pub",
|
|
"old_public_key_algo": "Ed25519",
|
|
"old_signature": "old-sig",
|
|
"timestamp": 123,
|
|
},
|
|
"timestamp": 101.0,
|
|
"sequence": 4,
|
|
"signature": "sig",
|
|
"public_key": "pub",
|
|
"public_key_algo": "Ed25519",
|
|
"protocol_version": "infonet/2",
|
|
}
|
|
|
|
|
|
def _public_gate_message_event() -> dict:
|
|
return {
|
|
"event_id": "gate-1",
|
|
"event_type": "gate_message",
|
|
"node_id": "!node-3",
|
|
"payload": {
|
|
"gate": "finance",
|
|
"ciphertext": "opaque",
|
|
"epoch": 2,
|
|
"nonce": "nonce-1",
|
|
"sender_ref": "sender-ref-1",
|
|
"format": "mls1",
|
|
},
|
|
"timestamp": 102.0,
|
|
"sequence": 5,
|
|
"signature": "sig",
|
|
"public_key": "pub",
|
|
"public_key_algo": "Ed25519",
|
|
"protocol_version": "infonet/2",
|
|
}
|
|
|
|
|
|
def test_private_gate_timestamp_is_stably_jittered_backward(monkeypatch):
|
|
class _Settings:
|
|
MESH_GATE_TIMESTAMP_JITTER_S = 60
|
|
|
|
monkeypatch.setattr(main, "get_settings", lambda: _Settings())
|
|
event = {
|
|
"event_id": "gate-event-stable-jitter",
|
|
"event_type": "gate_message",
|
|
"timestamp": 120.0,
|
|
"payload": {
|
|
"gate": "finance",
|
|
"ciphertext": "opaque",
|
|
"format": "mls1",
|
|
},
|
|
}
|
|
|
|
first = main._strip_gate_identity(event)
|
|
second = main._strip_gate_identity(dict(event))
|
|
|
|
assert first["timestamp"] == second["timestamp"]
|
|
assert 60.0 <= float(first["timestamp"]) < 120.0
|
|
assert "public_key" not in first
|
|
assert "node_id" not in first
|
|
|
|
|
|
def test_gate_identity_redaction_keeps_member_payload_fields_only():
|
|
event = {
|
|
"event_id": "gate-event-visible-fields",
|
|
"event_type": "gate_message",
|
|
"timestamp": 120.0,
|
|
"node_id": "!sb_gate_member",
|
|
"sequence": 7,
|
|
"signature": "sig",
|
|
"public_key": "pub",
|
|
"public_key_algo": "Ed25519",
|
|
"protocol_version": "infonet/2",
|
|
"payload": {
|
|
"gate": "finance",
|
|
"ciphertext": "opaque",
|
|
"format": "mls1",
|
|
"nonce": "nonce-1",
|
|
"sender_ref": "sender-ref-1",
|
|
},
|
|
}
|
|
|
|
stripped = main._strip_gate_identity(event)
|
|
|
|
assert stripped["protocol_version"] == "infonet/2"
|
|
assert stripped["payload"]["nonce"] == "nonce-1"
|
|
assert stripped["payload"]["sender_ref"] == "sender-ref-1"
|
|
assert "node_id" not in stripped
|
|
assert "public_key" not in stripped
|
|
assert "public_key_algo" not in stripped
|
|
assert "sequence" not in stripped
|
|
assert "signature" not in stripped
|
|
|
|
|
|
class _FakePublicInfonet:
|
|
def __init__(self):
|
|
self.head_hash = "head-1"
|
|
self.events = [_vote_event(), _key_rotate_event(), _public_gate_message_event()]
|
|
|
|
@staticmethod
|
|
def _limit_value(limit: int) -> int:
|
|
try:
|
|
return int(limit)
|
|
except Exception:
|
|
return int(getattr(limit, "default", 100) or 100)
|
|
|
|
def decorate_event(self, evt: dict) -> dict:
|
|
return dict(evt)
|
|
|
|
def decorate_events(self, events: list[dict]) -> list[dict]:
|
|
return [dict(evt) for evt in events]
|
|
|
|
def get_event(self, event_id: str):
|
|
for evt in self.events:
|
|
if evt["event_id"] == event_id:
|
|
return dict(evt)
|
|
return None
|
|
|
|
def get_messages(self, gate_id: str = "", limit: int = 50, offset: int = 0):
|
|
resolved_limit = self._limit_value(limit)
|
|
return [dict(evt) for evt in self.events[offset : offset + resolved_limit]]
|
|
|
|
def get_events_by_node(self, node_id: str, limit: int = 50):
|
|
return [dict(evt) for evt in self.events if evt["node_id"] == node_id][:limit]
|
|
|
|
def get_events_by_type(self, event_type: str, limit: int = 50, offset: int = 0):
|
|
resolved_limit = self._limit_value(limit)
|
|
filtered = [dict(evt) for evt in self.events if evt["event_type"] == event_type]
|
|
return filtered[offset : offset + resolved_limit]
|
|
|
|
def get_events_after(self, after_hash: str, limit: int = 100):
|
|
resolved_limit = self._limit_value(limit)
|
|
return [dict(evt) for evt in self.events[:resolved_limit]]
|
|
|
|
def get_events_after_locator(self, locator: list[str], limit: int = 100):
|
|
resolved_limit = self._limit_value(limit)
|
|
return self.head_hash, 0, [dict(evt) for evt in self.events[:resolved_limit]]
|
|
|
|
def get_merkle_proofs(self, start_index: int, count: int):
|
|
return {"root": "merkle-root", "total": len(self.events), "start": start_index, "proofs": []}
|
|
|
|
def get_merkle_root(self):
|
|
return "merkle-root"
|
|
|
|
|
|
def test_gate_store_rejects_unverified_peer_events(tmp_path):
|
|
store = GateMessageStore(data_dir=str(tmp_path / "gate_messages"))
|
|
|
|
result = store.ingest_peer_events(
|
|
"finance",
|
|
[
|
|
{
|
|
"event_type": "gate_message",
|
|
"timestamp": time.time(),
|
|
"payload": {
|
|
"gate": "finance",
|
|
"ciphertext": "opaque-ciphertext",
|
|
"format": "mls1",
|
|
"nonce": "nonce-1",
|
|
"sender_ref": "sender-ref-1",
|
|
},
|
|
}
|
|
],
|
|
)
|
|
|
|
assert result == {"accepted": 0, "duplicates": 0, "rejected": 1}
|
|
assert store.get_messages("finance", limit=10) == []
|
|
|
|
|
|
def test_gate_store_forwarded_peer_ingest_allows_out_of_order_signed_sequences_today(tmp_path, monkeypatch):
|
|
class _GateManager:
|
|
def can_enter(self, sender_id, gate_id):
|
|
assert sender_id
|
|
assert gate_id == "finance"
|
|
return True, "Access granted"
|
|
|
|
monkeypatch.setattr(mesh_reputation, "gate_manager", _GateManager(), raising=False)
|
|
store = GateMessageStore(data_dir=str(tmp_path / "gate_messages"))
|
|
author_key = ed25519.Ed25519PrivateKey.generate()
|
|
|
|
first = _signed_gate_event(
|
|
"finance",
|
|
private_key=author_key,
|
|
sequence=7,
|
|
ciphertext="opaque-ciphertext-7",
|
|
nonce="nonce-7",
|
|
sender_ref="sender-ref-7",
|
|
)
|
|
second = _signed_gate_event(
|
|
"finance",
|
|
private_key=author_key,
|
|
sequence=3,
|
|
ciphertext="opaque-ciphertext-3",
|
|
nonce="nonce-3",
|
|
sender_ref="sender-ref-3",
|
|
)
|
|
|
|
result = store.ingest_peer_events("finance", [first, second])
|
|
|
|
assert result == {"accepted": 2, "duplicates": 0, "rejected": 0}
|
|
stored = store.get_messages("finance", limit=10)
|
|
assert {msg["payload"]["ciphertext"] for msg in stored} == {"opaque-ciphertext-7", "opaque-ciphertext-3"}
|
|
|
|
|
|
def test_gate_store_accepts_verified_peer_events_and_persists_sanitized_shape(tmp_path, monkeypatch):
|
|
class _GateManager:
|
|
def can_enter(self, sender_id, gate_id):
|
|
assert sender_id
|
|
assert gate_id == "finance"
|
|
return True, "Access granted"
|
|
|
|
monkeypatch.setattr(mesh_reputation, "gate_manager", _GateManager(), raising=False)
|
|
store = GateMessageStore(data_dir=str(tmp_path / "gate_messages"))
|
|
|
|
signed = _signed_gate_event("finance")
|
|
result = store.ingest_peer_events("finance", [signed])
|
|
|
|
assert result == {"accepted": 1, "duplicates": 0, "rejected": 0}
|
|
stored = store.get_messages("finance", limit=1)[0]
|
|
assert stored["payload"] == {
|
|
"gate": "finance",
|
|
"ciphertext": "opaque-ciphertext",
|
|
"nonce": "nonce-1",
|
|
"sender_ref": "sender-ref-1",
|
|
"format": "mls1",
|
|
}
|
|
assert stored["node_id"] == signed["node_id"]
|
|
assert stored["public_key"] == signed["public_key"]
|
|
assert stored["public_key_algo"] == signed["public_key_algo"]
|
|
assert stored["signature"] == signed["signature"]
|
|
assert stored["sequence"] == signed["sequence"]
|
|
|
|
|
|
def test_gate_store_rejects_verified_peer_events_from_unauthorized_authors(tmp_path, monkeypatch):
|
|
class _GateManager:
|
|
def can_enter(self, sender_id, gate_id):
|
|
assert sender_id
|
|
assert gate_id == "finance"
|
|
return False, "Need 10 overall rep"
|
|
|
|
monkeypatch.setattr(mesh_reputation, "gate_manager", _GateManager(), raising=False)
|
|
store = GateMessageStore(data_dir=str(tmp_path / "gate_messages"))
|
|
|
|
result = store.ingest_peer_events("finance", [_signed_gate_event("finance")])
|
|
|
|
assert result == {"accepted": 0, "duplicates": 0, "rejected": 1}
|
|
assert store.get_messages("finance", limit=10) == []
|
|
|
|
|
|
def test_mesh_log_hides_private_entries_from_public_callers(monkeypatch):
|
|
from services.mesh.mesh_router import mesh_router
|
|
|
|
monkeypatch.setattr(
|
|
mesh_router,
|
|
"message_log",
|
|
deque(
|
|
[
|
|
{
|
|
"sender": "alice",
|
|
"destination": "broadcast",
|
|
"routed_via": "internet",
|
|
"priority": "normal",
|
|
"route_reason": "public",
|
|
"timestamp": 123.0,
|
|
"trust_tier": "public_degraded",
|
|
},
|
|
{
|
|
"message_id": "m-private",
|
|
"routed_via": "tor_arti",
|
|
"priority": "normal",
|
|
"route_reason": "private",
|
|
"timestamp": 456.0,
|
|
"trust_tier": "private_strong",
|
|
},
|
|
],
|
|
maxlen=8,
|
|
),
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
|
|
response = asyncio.run(main.mesh_log(_request("/api/mesh/log")))
|
|
|
|
assert response == {
|
|
"log": [
|
|
{
|
|
"sender": "alice",
|
|
"destination": "broadcast",
|
|
"routed_via": "internet",
|
|
"priority": "normal",
|
|
"route_reason": "public",
|
|
"timestamp": 123.0,
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
def test_mesh_status_public_hides_private_activity_volume(monkeypatch):
|
|
from services.mesh.mesh_router import mesh_router
|
|
from services import sigint_bridge
|
|
|
|
monkeypatch.setattr(
|
|
mesh_router,
|
|
"message_log",
|
|
deque(
|
|
[
|
|
{
|
|
"sender": "alice",
|
|
"destination": "broadcast",
|
|
"routed_via": "internet",
|
|
"priority": "normal",
|
|
"route_reason": "public",
|
|
"timestamp": 123.0,
|
|
"trust_tier": "public_degraded",
|
|
},
|
|
{
|
|
"message_id": "m-private",
|
|
"routed_via": "tor_arti",
|
|
"priority": "normal",
|
|
"route_reason": "private",
|
|
"timestamp": 456.0,
|
|
"trust_tier": "private_strong",
|
|
},
|
|
],
|
|
maxlen=8,
|
|
),
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(
|
|
sigint_bridge,
|
|
"sigint_grid",
|
|
type("FakeSigintGrid", (), {"get_all_signals": staticmethod(lambda: [])})(),
|
|
)
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = asyncio.run(main.mesh_status(_request("/api/mesh/status")))
|
|
|
|
assert response == {"message_log_size": 1}
|
|
|
|
|
|
def test_mesh_status_admin_gets_full_log_size_and_warning_details(monkeypatch):
|
|
from services.mesh.mesh_router import mesh_router
|
|
from services import sigint_bridge
|
|
now = time.time()
|
|
|
|
monkeypatch.setattr(
|
|
mesh_router,
|
|
"message_log",
|
|
deque(
|
|
[
|
|
{"timestamp": now, "trust_tier": "public_degraded"},
|
|
{"timestamp": now, "trust_tier": "private_transitional"},
|
|
],
|
|
maxlen=8,
|
|
),
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (True, "ok"))
|
|
monkeypatch.setattr(
|
|
sigint_bridge,
|
|
"sigint_grid",
|
|
type("FakeSigintGrid", (), {"get_all_signals": staticmethod(lambda: [])})(),
|
|
)
|
|
|
|
response = asyncio.run(main.mesh_status(_request("/api/mesh/status")))
|
|
|
|
assert response["message_log_size"] == 2
|
|
assert response["public_message_log_size"] == 1
|
|
assert "private_log_retention_seconds" in response
|
|
assert isinstance(response["security_warnings"], list)
|
|
|
|
|
|
def test_public_oracle_profile_hides_behavioral_lists(monkeypatch):
|
|
from services.mesh import mesh_oracle
|
|
|
|
fake_profile = {
|
|
"node_id": "!oracle",
|
|
"oracle_rep": 2.5,
|
|
"oracle_rep_total": 3.0,
|
|
"oracle_rep_locked": 0.5,
|
|
"predictions_won": 4,
|
|
"predictions_lost": 1,
|
|
"win_rate": 80,
|
|
"farming_pct": 25,
|
|
"active_stakes": [{"message_id": "msg-1", "side": "truth", "amount": 0.5, "expires": 123}],
|
|
"prediction_history": [{"market": "Test", "side": "yes", "rep_earned": 0.7}],
|
|
}
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(
|
|
mesh_oracle,
|
|
"oracle_ledger",
|
|
type("FakeOracleLedger", (), {"get_oracle_profile": staticmethod(lambda *_args, **_kwargs: dict(fake_profile))})(),
|
|
raising=False,
|
|
)
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = asyncio.run(main.oracle_profile(_request("/api/mesh/oracle/profile"), node_id="!oracle"))
|
|
|
|
assert response["node_id"] == "!oracle"
|
|
assert response["oracle_rep"] == 2.5
|
|
assert response["active_stakes"] == []
|
|
assert response["prediction_history"] == []
|
|
|
|
|
|
def test_public_oracle_predictions_hide_active_positions(monkeypatch):
|
|
from services.mesh import mesh_oracle
|
|
|
|
active_predictions = [
|
|
{"prediction_id": "pred-1", "market_title": "Alpha", "side": "yes", "mode": "free"},
|
|
{"prediction_id": "pred-2", "market_title": "Bravo", "side": "no", "mode": "staked"},
|
|
]
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(
|
|
mesh_oracle,
|
|
"oracle_ledger",
|
|
type(
|
|
"FakeOracleLedger",
|
|
(),
|
|
{"get_active_predictions": staticmethod(lambda *_args, **_kwargs: list(active_predictions))},
|
|
)(),
|
|
raising=False,
|
|
)
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = asyncio.run(
|
|
main.oracle_predictions(_request("/api/mesh/oracle/predictions"), node_id="!oracle")
|
|
)
|
|
|
|
assert response == {"predictions": [], "count": 2}
|
|
|
|
|
|
def test_public_oracle_stakes_hide_staker_lists(monkeypatch):
|
|
from services.mesh import mesh_oracle
|
|
|
|
fake_stakes = {
|
|
"message_id": "msg-1",
|
|
"truth_total": 1.5,
|
|
"false_total": 0.75,
|
|
"truth_stakers": [{"node_id": "!truth", "amount": 1.5, "expires": 123}],
|
|
"false_stakers": [{"node_id": "!false", "amount": 0.75, "expires": 456}],
|
|
"earliest_expiry": 123,
|
|
}
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(
|
|
mesh_oracle,
|
|
"oracle_ledger",
|
|
type(
|
|
"FakeOracleLedger",
|
|
(),
|
|
{"get_stakes_for_message": staticmethod(lambda *_args, **_kwargs: dict(fake_stakes))},
|
|
)(),
|
|
raising=False,
|
|
)
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = asyncio.run(
|
|
main.oracle_stakes_for_message(_request("/api/mesh/oracle/stakes/msg-1"), message_id="msg-1")
|
|
)
|
|
|
|
assert response == {
|
|
"message_id": "msg-1",
|
|
"truth_total": 1.5,
|
|
"false_total": 0.75,
|
|
"truth_stakers": [],
|
|
"false_stakers": [],
|
|
"earliest_expiry": 123,
|
|
}
|
|
|
|
|
|
def test_public_wormhole_settings_redact_sensitive_fields(monkeypatch):
|
|
monkeypatch.setattr(
|
|
main,
|
|
"read_wormhole_settings",
|
|
lambda: {
|
|
"enabled": True,
|
|
"transport": "tor",
|
|
"anonymous_mode": True,
|
|
"socks_proxy": "127.0.0.1:9050",
|
|
"socks_dns": True,
|
|
"privacy_profile": "high",
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = asyncio.run(main.api_get_wormhole_settings(_request("/api/settings/wormhole")))
|
|
|
|
assert response == {
|
|
"enabled": True,
|
|
"transport": "tor",
|
|
"anonymous_mode": True,
|
|
}
|
|
|
|
|
|
def test_admin_wormhole_settings_keep_sensitive_fields(monkeypatch):
|
|
monkeypatch.setattr(
|
|
main,
|
|
"read_wormhole_settings",
|
|
lambda: {
|
|
"enabled": True,
|
|
"transport": "tor",
|
|
"anonymous_mode": True,
|
|
"socks_proxy": "127.0.0.1:9050",
|
|
"socks_dns": True,
|
|
"privacy_profile": "high",
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (True, "ok"))
|
|
|
|
response = asyncio.run(main.api_get_wormhole_settings(_request("/api/settings/wormhole")))
|
|
|
|
assert response["enabled"] is True
|
|
assert response["socks_proxy"] == "127.0.0.1:9050"
|
|
assert response["socks_dns"] is True
|
|
assert response["privacy_profile"] == "high"
|
|
|
|
|
|
def test_public_privacy_profile_hides_transport_metadata(monkeypatch):
|
|
monkeypatch.setattr(
|
|
main,
|
|
"read_wormhole_settings",
|
|
lambda: {
|
|
"enabled": True,
|
|
"privacy_profile": "high",
|
|
"transport": "tor",
|
|
"anonymous_mode": True,
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = asyncio.run(main.api_get_privacy_profile(_request("/api/settings/privacy-profile")))
|
|
|
|
assert response == {
|
|
"profile": "high",
|
|
"wormhole_enabled": True,
|
|
}
|
|
|
|
|
|
def test_public_settings_wormhole_status_uses_redacted_shape(monkeypatch):
|
|
from httpx import ASGITransport, AsyncClient
|
|
|
|
monkeypatch.setattr(
|
|
main,
|
|
"get_wormhole_state",
|
|
lambda: {
|
|
"installed": True,
|
|
"configured": True,
|
|
"running": True,
|
|
"ready": True,
|
|
"last_error": "sensitive",
|
|
"proxy_active": "tor",
|
|
"arti_ready": True,
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_current_private_lane_tier", lambda *_args, **_kwargs: "private_strong")
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
async def _run():
|
|
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
|
|
response = await ac.get("/api/settings/wormhole-status")
|
|
return response.json()
|
|
|
|
response = asyncio.run(_run())
|
|
|
|
assert response == {
|
|
"installed": True,
|
|
"configured": True,
|
|
"running": True,
|
|
"ready": True,
|
|
}
|
|
|
|
|
|
def test_authenticated_settings_wormhole_status_includes_privacy_core_attestation(monkeypatch):
|
|
import auth
|
|
from httpx import ASGITransport, AsyncClient
|
|
from services.config import get_settings
|
|
|
|
monkeypatch.setenv("MESH_PRIVATE_CLEARNET_FALLBACK", "block")
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_NODE_ID_COMPAT", "true")
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "true")
|
|
monkeypatch.setenv("MESH_ALLOW_COMPAT_DM_INVITE_IMPORT", "false")
|
|
monkeypatch.setenv("MESH_RELEASE_DM_RELAY_SECURITY_SUITE_GREEN", "true")
|
|
get_settings.cache_clear()
|
|
monkeypatch.setattr(
|
|
auth,
|
|
"_anonymous_mode_state",
|
|
lambda: {
|
|
"enabled": True,
|
|
"wormhole_enabled": True,
|
|
"ready": True,
|
|
"effective_transport": "tor_arti",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
auth,
|
|
"_external_assurance_status_snapshot",
|
|
lambda: {
|
|
"current": True,
|
|
"configured": True,
|
|
"state": "current_external",
|
|
"detail": "configured external assurance is current",
|
|
"witness_state": "current",
|
|
"transparency_state": "current",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"get_wormhole_state",
|
|
lambda: {
|
|
"installed": True,
|
|
"configured": True,
|
|
"running": True,
|
|
"ready": True,
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_current_private_lane_tier", lambda *_args, **_kwargs: "private_strong")
|
|
monkeypatch.setattr(main, "_scoped_view_authenticated", lambda *_args, **_kwargs: True)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"_privacy_core_status",
|
|
lambda: {
|
|
"available": True,
|
|
"version": "privacy-core-test",
|
|
"library_path": "C:/privacy-core/target/release/privacy_core.dll",
|
|
"library_sha256": "ab" * 32,
|
|
"policy_ok": True,
|
|
},
|
|
)
|
|
|
|
async def _run():
|
|
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
|
|
response = await ac.get("/api/settings/wormhole-status")
|
|
return response.json()
|
|
|
|
response = asyncio.run(_run())
|
|
|
|
assert response["transport_tier"] == "private_strong"
|
|
assert response["strong_claims"]["allowed"] is True
|
|
assert response["privacy_core"]["available"] is True
|
|
assert response["privacy_core"]["version"] == "privacy-core-test"
|
|
assert response["privacy_core"]["library_sha256"] == "ab" * 32
|
|
assert response["release_gate"]["ready"] is True
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["ok"] is True
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["source"] == "env"
|
|
assert response["release_gate"]["criteria"]["privacy_core_pinned"]["ok"] is True
|
|
assert response["release_gate"]["criteria"]["external_assurance_current"]["ok"] is True
|
|
assert response["release_gate"]["threat_model_reference"] == "docs/mesh/threat-model.md"
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_authenticated_settings_wormhole_status_prefers_release_attestation_file(
|
|
monkeypatch, tmp_path
|
|
):
|
|
import auth
|
|
from httpx import ASGITransport, AsyncClient
|
|
from services.config import get_settings
|
|
|
|
attestation_path = tmp_path / "release_attestation.json"
|
|
attestation_path.write_text(
|
|
json.dumps(
|
|
{
|
|
"generated_at": "2026-04-14T18:30:00Z",
|
|
"commit": "abc1234",
|
|
"threat_model_reference": "docs/mesh/threat-model.md",
|
|
"dm_relay_security_suite": {
|
|
"name": "dm_relay_security",
|
|
"green": True,
|
|
"detail": "CI attestation confirms the DM relay security suite is green",
|
|
"report": "artifacts/dm-relay-security-report.txt",
|
|
},
|
|
"ci": {
|
|
"workflow": "CI",
|
|
"run_id": "12345",
|
|
"run_attempt": "2",
|
|
"ref": "refs/heads/main",
|
|
},
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setenv("MESH_PRIVATE_CLEARNET_FALLBACK", "block")
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_NODE_ID_COMPAT", "true")
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "true")
|
|
monkeypatch.setenv("MESH_RELEASE_ATTESTATION_PATH", str(attestation_path))
|
|
monkeypatch.setenv("MESH_RELEASE_DM_RELAY_SECURITY_SUITE_GREEN", "false")
|
|
get_settings.cache_clear()
|
|
monkeypatch.setattr(
|
|
auth,
|
|
"_anonymous_mode_state",
|
|
lambda: {
|
|
"enabled": True,
|
|
"wormhole_enabled": True,
|
|
"ready": True,
|
|
"effective_transport": "tor_arti",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
auth,
|
|
"_external_assurance_status_snapshot",
|
|
lambda: {
|
|
"current": True,
|
|
"configured": True,
|
|
"state": "current_external",
|
|
"detail": "configured external assurance is current",
|
|
"witness_state": "current",
|
|
"transparency_state": "current",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"get_wormhole_state",
|
|
lambda: {
|
|
"installed": True,
|
|
"configured": True,
|
|
"running": True,
|
|
"ready": True,
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_current_private_lane_tier", lambda *_args, **_kwargs: "private_strong")
|
|
monkeypatch.setattr(main, "_scoped_view_authenticated", lambda *_args, **_kwargs: True)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"_privacy_core_status",
|
|
lambda: {
|
|
"available": True,
|
|
"version": "privacy-core-test",
|
|
"library_path": "C:/privacy-core/target/release/privacy_core.dll",
|
|
"library_sha256": "ab" * 32,
|
|
"policy_ok": True,
|
|
},
|
|
)
|
|
|
|
async def _run():
|
|
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
|
|
response = await ac.get("/api/settings/wormhole-status")
|
|
return response.json()
|
|
|
|
response = asyncio.run(_run())
|
|
|
|
assert response["release_gate"]["ready"] is True
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["ok"] is True
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["source"] == "file"
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["commit"] == "abc1234"
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["suite_report"] == "artifacts/dm-relay-security-report.txt"
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["workflow"] == "CI"
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["run_id"] == "12345"
|
|
assert response["release_gate"]["attestation"]["path"] == str(attestation_path)
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_authenticated_settings_wormhole_status_fails_closed_for_missing_explicit_release_attestation(
|
|
monkeypatch, tmp_path
|
|
):
|
|
import auth
|
|
from httpx import ASGITransport, AsyncClient
|
|
from services.config import get_settings
|
|
|
|
monkeypatch.setenv("MESH_PRIVATE_CLEARNET_FALLBACK", "block")
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_NODE_ID_COMPAT", "true")
|
|
monkeypatch.setenv("MESH_BLOCK_LEGACY_AGENT_ID_LOOKUP", "true")
|
|
monkeypatch.setenv("MESH_RELEASE_ATTESTATION_PATH", str(tmp_path / "missing_release_attestation.json"))
|
|
monkeypatch.setenv("MESH_RELEASE_DM_RELAY_SECURITY_SUITE_GREEN", "true")
|
|
get_settings.cache_clear()
|
|
monkeypatch.setattr(
|
|
auth,
|
|
"_anonymous_mode_state",
|
|
lambda: {
|
|
"enabled": True,
|
|
"wormhole_enabled": True,
|
|
"ready": True,
|
|
"effective_transport": "tor_arti",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
auth,
|
|
"_external_assurance_status_snapshot",
|
|
lambda: {
|
|
"current": True,
|
|
"configured": True,
|
|
"state": "current_external",
|
|
"detail": "configured external assurance is current",
|
|
"witness_state": "current",
|
|
"transparency_state": "current",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"get_wormhole_state",
|
|
lambda: {
|
|
"installed": True,
|
|
"configured": True,
|
|
"running": True,
|
|
"ready": True,
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_current_private_lane_tier", lambda *_args, **_kwargs: "private_strong")
|
|
monkeypatch.setattr(main, "_scoped_view_authenticated", lambda *_args, **_kwargs: True)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"_privacy_core_status",
|
|
lambda: {
|
|
"available": True,
|
|
"version": "privacy-core-test",
|
|
"library_path": "C:/privacy-core/target/release/privacy_core.dll",
|
|
"library_sha256": "ab" * 32,
|
|
"policy_ok": True,
|
|
},
|
|
)
|
|
|
|
async def _run():
|
|
async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
|
|
response = await ac.get("/api/settings/wormhole-status")
|
|
return response.json()
|
|
|
|
response = asyncio.run(_run())
|
|
|
|
assert response["release_gate"]["ready"] is False
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["ok"] is False
|
|
assert response["release_gate"]["criteria"]["dm_relay_security_suite_green"]["source"] == "file_missing"
|
|
assert response["release_gate"]["blocking_reasons"][0] == "dm_relay_security_suite_green"
|
|
get_settings.cache_clear()
|
|
|
|
|
|
def test_public_wormhole_status_hides_privacy_core_attestation(monkeypatch):
|
|
monkeypatch.setattr(
|
|
main,
|
|
"get_wormhole_state",
|
|
lambda: {
|
|
"installed": True,
|
|
"configured": True,
|
|
"running": True,
|
|
"ready": True,
|
|
},
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"_privacy_core_status",
|
|
lambda: {
|
|
"available": True,
|
|
"version": "privacy-core-test",
|
|
"library_path": "C:/privacy-core/target/release/privacy_core.dll",
|
|
"library_sha256": "ab" * 32,
|
|
},
|
|
)
|
|
|
|
response = asyncio.run(main.api_wormhole_status(_request("/api/wormhole/status")))
|
|
|
|
assert "privacy_core" not in response
|
|
assert "strong_claims" not in response
|
|
assert "legacy_compatibility" not in response
|
|
assert "release_gate" not in response
|
|
|
|
|
|
def test_public_infonet_status_hides_private_lane_policy(monkeypatch):
|
|
monkeypatch.setattr(
|
|
main,
|
|
"_check_scoped_auth",
|
|
lambda *_args, **_kwargs: (False, "no"),
|
|
)
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = asyncio.run(main.infonet_status(_request("/api/mesh/infonet/status")))
|
|
|
|
assert "private_lane_tier" not in response
|
|
assert "private_lane_policy" not in response
|
|
assert "network_id" in response
|
|
|
|
|
|
def test_public_rns_status_hides_private_lane_policy(monkeypatch):
|
|
from services import wormhole_supervisor
|
|
from services.mesh import mesh_rns
|
|
|
|
monkeypatch.setattr(
|
|
main,
|
|
"_check_scoped_auth",
|
|
lambda *_args, **_kwargs: (False, "no"),
|
|
)
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
monkeypatch.setattr(
|
|
main,
|
|
"_current_private_lane_tier",
|
|
lambda *_args, **_kwargs: "private_strong",
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
wormhole_supervisor,
|
|
"get_wormhole_state",
|
|
lambda: {"configured": True, "ready": True, "arti_ready": True, "rns_ready": True},
|
|
)
|
|
monkeypatch.setattr(
|
|
mesh_rns,
|
|
"rns_bridge",
|
|
type(
|
|
"FakeRnsBridge",
|
|
(),
|
|
{
|
|
"status": staticmethod(
|
|
lambda: {
|
|
"enabled": True,
|
|
"ready": True,
|
|
"configured_peers": 3,
|
|
"active_peers": 2,
|
|
"local_hash": "abc123",
|
|
"session_identities": 4,
|
|
"destination_age_s": 90,
|
|
"private_dm_direct_ready": True,
|
|
}
|
|
)
|
|
},
|
|
)(),
|
|
)
|
|
|
|
response = asyncio.run(main.mesh_rns_status(_request("/api/mesh/rns/status")))
|
|
|
|
assert response == {
|
|
"enabled": True,
|
|
"ready": True,
|
|
"configured_peers": 3,
|
|
"active_peers": 2,
|
|
}
|
|
|
|
|
|
def test_public_dm_witness_hides_graph_details(monkeypatch):
|
|
from services.mesh import mesh_dm_relay
|
|
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
monkeypatch.setattr(
|
|
mesh_dm_relay,
|
|
"dm_relay",
|
|
type(
|
|
"FakeRelay",
|
|
(),
|
|
{"get_witnesses": staticmethod(lambda *_args, **_kwargs: [{"witness_id": "!alpha"}])},
|
|
)(),
|
|
raising=False,
|
|
)
|
|
|
|
response = asyncio.run(
|
|
main.dm_key_witness_get(
|
|
_request("/api/mesh/dm/witness"),
|
|
target_id="!target",
|
|
dh_pub_key="dh-pub",
|
|
)
|
|
)
|
|
|
|
assert response == {"ok": True, "count": 1}
|
|
|
|
|
|
def test_audit_dm_witness_keeps_graph_details(monkeypatch):
|
|
from services.mesh import mesh_dm_relay
|
|
|
|
witnesses = [{"witness_id": "!alpha"}, {"witness_id": "!bravo"}]
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (True, "ok"))
|
|
monkeypatch.setattr(
|
|
mesh_dm_relay,
|
|
"dm_relay",
|
|
type(
|
|
"FakeRelay",
|
|
(),
|
|
{"get_witnesses": staticmethod(lambda *_args, **_kwargs: witnesses)},
|
|
)(),
|
|
raising=False,
|
|
)
|
|
|
|
response = asyncio.run(
|
|
main.dm_key_witness_get(
|
|
_request("/api/mesh/dm/witness"),
|
|
target_id="!target",
|
|
dh_pub_key="dh-pub",
|
|
)
|
|
)
|
|
|
|
assert response == {
|
|
"ok": True,
|
|
"target_id": "!target",
|
|
"dh_pub_key": "dh-pub",
|
|
"count": 2,
|
|
"witnesses": witnesses,
|
|
}
|
|
|
|
|
|
def test_gate_compose_redaction_helper_hides_signer_fields():
|
|
response = main._redact_composed_gate_message(
|
|
{
|
|
"ok": True,
|
|
"gate_id": "finance",
|
|
"identity_scope": "gate_persona",
|
|
"sender_id": "!gate-sender",
|
|
"public_key": "public-key",
|
|
"public_key_algo": "Ed25519",
|
|
"protocol_version": "infonet/2",
|
|
"sequence": 42,
|
|
"signature": "deadbeef",
|
|
"ciphertext": "ciphertext",
|
|
"nonce": "nonce",
|
|
"sender_ref": "sender-ref",
|
|
"format": "mls1",
|
|
"timestamp": 123.0,
|
|
"epoch": 3,
|
|
}
|
|
)
|
|
|
|
assert response == {
|
|
"ok": True,
|
|
"gate_id": "finance",
|
|
"identity_scope": "gate_persona",
|
|
"ciphertext": "ciphertext",
|
|
"nonce": "nonce",
|
|
"sender_ref": "sender-ref",
|
|
"format": "mls1",
|
|
"timestamp": 123.0,
|
|
"epoch": 3,
|
|
}
|
|
|
|
|
|
def test_dm_relay_auto_msg_id_omits_sender_suffix(tmp_path, monkeypatch):
|
|
from services.mesh import mesh_dm_relay
|
|
|
|
monkeypatch.setattr(mesh_dm_relay, "DATA_DIR", tmp_path, raising=False)
|
|
monkeypatch.setattr(mesh_dm_relay, "RELAY_FILE", tmp_path / "dm_relay.json", raising=False)
|
|
relay = mesh_dm_relay.DMRelay()
|
|
|
|
result = relay.deposit(
|
|
sender_id="!alice-1234",
|
|
recipient_id="!bob",
|
|
ciphertext="ciphertext",
|
|
delivery_class="request",
|
|
sender_token_hash="sender-token-hash",
|
|
)
|
|
|
|
assert result["ok"] is True
|
|
assert str(result["msg_id"]).startswith("dm_")
|
|
assert "1234" not in str(result["msg_id"])
|
|
|
|
|
|
def test_public_event_endpoints_preserve_redactions(client, monkeypatch):
|
|
from services.mesh import mesh_hashchain
|
|
|
|
fake_infonet = _FakePublicInfonet()
|
|
monkeypatch.setattr(mesh_hashchain, "infonet", fake_infonet, raising=False)
|
|
monkeypatch.setattr(
|
|
mesh_hashchain,
|
|
"gate_store",
|
|
type("FakeGateStore", (), {"get_event": staticmethod(lambda *_args, **_kwargs: None)})(),
|
|
raising=False,
|
|
)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
collection_responses = [
|
|
client.get("/api/mesh/infonet/messages").json()["messages"],
|
|
client.get("/api/mesh/infonet/events").json()["events"],
|
|
client.get("/api/mesh/infonet/sync").json()["events"],
|
|
asyncio.run(
|
|
main.infonet_sync_post(
|
|
_json_request("/api/mesh/infonet/sync", {"locator": ["head-1"]})
|
|
)
|
|
)["events"],
|
|
]
|
|
node_response = client.get("/api/mesh/infonet/node/!node-1").json()["events"]
|
|
single_event = client.get("/api/mesh/infonet/event/rotate-1").json()
|
|
|
|
for events in collection_responses:
|
|
assert all(evt.get("event_type") != "gate_message" for evt in events)
|
|
vote = next(evt for evt in events if evt.get("event_type") == "vote")
|
|
rotate = next(evt for evt in events if evt.get("event_type") == "key_rotate")
|
|
assert "gate" not in vote.get("payload", {})
|
|
assert "old_node_id" not in rotate.get("payload", {})
|
|
assert "old_public_key" not in rotate.get("payload", {})
|
|
assert "old_signature" not in rotate.get("payload", {})
|
|
|
|
assert len(node_response) == 1
|
|
assert node_response[0]["event_type"] == "vote"
|
|
assert set(node_response[0].keys()) == {"event_id", "event_type", "timestamp"}
|
|
|
|
assert single_event["event_type"] == "key_rotate"
|
|
assert "old_node_id" not in single_event.get("payload", {})
|
|
|
|
|
|
def test_mesh_router_private_log_entries_age_out(monkeypatch):
|
|
from services import config as config_mod
|
|
from services.mesh.mesh_router import MeshRouter
|
|
|
|
monkeypatch.setattr(
|
|
config_mod,
|
|
"get_settings",
|
|
lambda: type("Settings", (), {"MESH_PRIVATE_LOG_TTL_S": 60})(),
|
|
)
|
|
|
|
router = MeshRouter()
|
|
router.message_log = deque(
|
|
[
|
|
{"timestamp": 100.0, "trust_tier": "private_strong"},
|
|
{"timestamp": 100.0, "trust_tier": "public_degraded"},
|
|
{"timestamp": 180.0, "trust_tier": "private_transitional"},
|
|
],
|
|
maxlen=8,
|
|
)
|
|
|
|
router.prune_message_log(now=200.0)
|
|
|
|
assert list(router.message_log) == [
|
|
{"timestamp": 100.0, "trust_tier": "public_degraded"},
|
|
{"timestamp": 180.0, "trust_tier": "private_transitional"},
|
|
]
|
|
|
|
|
|
def test_mesh_router_private_log_entries_strip_metadata(caplog, monkeypatch):
|
|
from services.mesh import mesh_router as mesh_router_mod
|
|
from services.mesh.mesh_router import MeshEnvelope, MeshRouter, Priority, TransportResult
|
|
|
|
monkeypatch.setattr(mesh_router_mod, "_supervisor_verified_trust_tier", lambda: "private_strong")
|
|
router = MeshRouter()
|
|
envelope = MeshEnvelope(
|
|
sender_id="alice",
|
|
destination="bob",
|
|
channel="shadow",
|
|
priority=Priority.NORMAL,
|
|
trust_tier="private_strong",
|
|
payload="secret payload",
|
|
)
|
|
envelope.routed_via = "tor_arti"
|
|
envelope.route_reason = "PRIVATE_STRONG — Tor required"
|
|
|
|
with caplog.at_level(logging.INFO, logger="services.mesh_router"):
|
|
router._log(envelope, [TransportResult(True, "tor_arti", "Delivered to 1 peer via Tor")])
|
|
|
|
entry = list(router.message_log)[0]
|
|
assert entry["trust_tier"] == "private_strong"
|
|
assert entry["routed_via"] == "tor_arti"
|
|
assert entry["transport_outcomes"] == [{"transport": "tor_arti", "ok": True}]
|
|
assert "message_id" not in entry
|
|
assert "channel" not in entry
|
|
assert "payload_type" not in entry
|
|
assert "payload_bytes" not in entry
|
|
assert "results" not in entry
|
|
assert envelope.message_id not in caplog.text
|
|
|
|
|
|
def test_mesh_metrics_requires_audit_scope(client, monkeypatch):
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "Forbidden — invalid or missing admin key"))
|
|
|
|
response = client.get("/api/mesh/metrics")
|
|
|
|
assert response.status_code == 403
|
|
assert response.json()["detail"] == "Forbidden — invalid or missing admin key"
|
|
|
|
|
|
def test_mesh_metrics_allows_audit_scope(client, monkeypatch):
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (True, "ok"))
|
|
|
|
response = client.get("/api/mesh/metrics")
|
|
|
|
assert response.status_code == 200
|
|
assert "counters" in response.json()
|
|
|
|
|
|
def test_dm_send_rejects_unsealed_shared_private_dm(monkeypatch):
|
|
from services import wormhole_supervisor
|
|
|
|
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_strong")
|
|
|
|
response = asyncio.run(
|
|
main.dm_send(
|
|
_json_request(
|
|
"/api/mesh/dm/send",
|
|
{
|
|
"sender_id": "alice",
|
|
"recipient_id": "bob",
|
|
"delivery_class": "shared",
|
|
"recipient_token": "shared-mailbox-token",
|
|
"ciphertext": "ciphertext",
|
|
"msg_id": "dm-shared-1",
|
|
"timestamp": int(time.time()),
|
|
"public_key": "cHVi",
|
|
"public_key_algo": "Ed25519",
|
|
"signature": "sig",
|
|
"sequence": 1,
|
|
"protocol_version": "infonet/2",
|
|
},
|
|
)
|
|
)
|
|
)
|
|
|
|
assert response == {"ok": False, "detail": "sealed sender required for shared private DMs"}
|
|
|
|
|
|
def test_dm_key_package_error_detail_is_sanitized(monkeypatch):
|
|
from services.mesh import mesh_dm_mls
|
|
|
|
def _raise(*_args, **_kwargs):
|
|
raise RuntimeError("sensitive backend detail")
|
|
|
|
monkeypatch.setattr(mesh_dm_mls, "_identity_handle_for_alias", _raise)
|
|
|
|
response = mesh_dm_mls.export_dm_key_package_for_alias("alpha")
|
|
|
|
assert response == {"ok": False, "detail": "dm_mls_key_package_failed"}
|
|
|
|
|
|
def test_gate_compose_error_detail_is_sanitized(monkeypatch):
|
|
from services.mesh import mesh_gate_mls
|
|
from services import wormhole_supervisor
|
|
|
|
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
|
|
monkeypatch.setattr(
|
|
mesh_gate_mls,
|
|
"_active_gate_member",
|
|
lambda *_args, **_kwargs: ({"persona_id": "p1"}, "member"),
|
|
)
|
|
monkeypatch.setattr(mesh_gate_mls, "_sync_binding", lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("sensitive gate detail")))
|
|
|
|
response = mesh_gate_mls.compose_encrypted_gate_message("finance", "hello")
|
|
|
|
assert response == {"ok": False, "detail": "gate_mls_compose_failed"}
|
|
|
|
|
|
def test_dm_alias_blob_sign_error_detail_is_sanitized(monkeypatch):
|
|
from services.mesh import mesh_wormhole_persona
|
|
|
|
monkeypatch.setattr(mesh_wormhole_persona, "bootstrap_wormhole_persona_state", lambda: None)
|
|
monkeypatch.setattr(
|
|
mesh_wormhole_persona,
|
|
"read_wormhole_persona_state",
|
|
lambda: {
|
|
"dm_identity": {
|
|
"private_key": "not-base64",
|
|
"public_key": "pub",
|
|
"public_key_algo": "Ed25519",
|
|
}
|
|
},
|
|
)
|
|
|
|
response = mesh_wormhole_persona.sign_dm_alias_blob("alpha", b"payload")
|
|
|
|
assert response == {"ok": False, "detail": "dm_alias_blob_sign_failed"}
|
|
|
|
|
|
def test_public_mesh_reputation_is_summary_only(client, monkeypatch):
|
|
from services.mesh import mesh_reputation as mesh_reputation_mod
|
|
|
|
class _FakeLedger:
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
def get_reputation_log(self, node_id, detailed=False):
|
|
self.calls.append((node_id, detailed))
|
|
payload = {
|
|
"node_id": node_id,
|
|
"overall": 7,
|
|
"upvotes": 3,
|
|
"downvotes": 1,
|
|
}
|
|
if detailed:
|
|
payload.update(
|
|
{
|
|
"gates": {"finance": 5},
|
|
"recent_votes": [{"voter": "blind-voter"}],
|
|
"node_age_days": 14.0,
|
|
"is_agent": True,
|
|
}
|
|
)
|
|
return payload
|
|
|
|
fake_ledger = _FakeLedger()
|
|
monkeypatch.setattr(mesh_reputation_mod, "reputation_ledger", fake_ledger, raising=False)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (False, "no"))
|
|
monkeypatch.setattr(main, "_is_debug_test_request", lambda *_args, **_kwargs: False)
|
|
|
|
response = client.get("/api/mesh/reputation?node_id=!alpha")
|
|
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"node_id": "!alpha",
|
|
"overall": 7,
|
|
"upvotes": 3,
|
|
"downvotes": 1,
|
|
}
|
|
assert fake_ledger.calls == [("!alpha", False)]
|
|
|
|
|
|
def test_audit_mesh_reputation_keeps_detailed_breakdown(client, monkeypatch):
|
|
from services.mesh import mesh_reputation as mesh_reputation_mod
|
|
|
|
class _FakeLedger:
|
|
def __init__(self):
|
|
self.calls = []
|
|
|
|
def get_reputation_log(self, node_id, detailed=False):
|
|
self.calls.append((node_id, detailed))
|
|
payload = {
|
|
"node_id": node_id,
|
|
"overall": 9,
|
|
"upvotes": 4,
|
|
"downvotes": 1,
|
|
}
|
|
if detailed:
|
|
payload.update(
|
|
{
|
|
"gates": {"finance": 6},
|
|
"recent_votes": [{"voter": "blind-voter"}],
|
|
"node_age_days": 21.0,
|
|
"is_agent": False,
|
|
}
|
|
)
|
|
return payload
|
|
|
|
fake_ledger = _FakeLedger()
|
|
monkeypatch.setattr(mesh_reputation_mod, "reputation_ledger", fake_ledger, raising=False)
|
|
monkeypatch.setattr(main, "_check_scoped_auth", lambda *_args, **_kwargs: (True, "ok"))
|
|
|
|
response = client.get("/api/mesh/reputation?node_id=!bravo")
|
|
|
|
assert response.status_code == 200
|
|
assert response.json()["gates"] == {"finance": 6}
|
|
assert response.json()["recent_votes"] == [{"voter": "blind-voter"}]
|
|
assert response.json()["node_age_days"] == 21.0
|
|
assert response.json()["is_agent"] is False
|
|
assert fake_ledger.calls == [("!bravo", True)]
|
|
|
|
|
|
def test_dm_mls_logs_only_hashed_aliases_on_failure(caplog, monkeypatch):
|
|
from services.mesh import mesh_dm_mls
|
|
|
|
def _raise(*_args, **_kwargs):
|
|
raise RuntimeError("sensitive backend detail")
|
|
|
|
monkeypatch.setattr(mesh_dm_mls, "_identity_handle_for_alias", _raise)
|
|
|
|
with caplog.at_level(logging.ERROR, logger="services.mesh.mesh_dm_mls"):
|
|
response = mesh_dm_mls.export_dm_key_package_for_alias("alpha-alias")
|
|
|
|
assert response == {"ok": False, "detail": "dm_mls_key_package_failed"}
|
|
assert "alpha-alias" not in caplog.text
|
|
assert "alias#" in caplog.text
|
|
|
|
|
|
def test_gate_mls_logs_only_hashed_gate_ids_on_failure(caplog, monkeypatch):
|
|
from services import wormhole_supervisor
|
|
from services.mesh import mesh_gate_mls
|
|
|
|
monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")
|
|
monkeypatch.setattr(
|
|
mesh_gate_mls,
|
|
"_active_gate_member",
|
|
lambda *_args, **_kwargs: ({"persona_id": "p1"}, "member"),
|
|
)
|
|
monkeypatch.setattr(
|
|
mesh_gate_mls,
|
|
"_sync_binding",
|
|
lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("gate failure")),
|
|
)
|
|
|
|
with caplog.at_level(logging.ERROR, logger="services.mesh.mesh_gate_mls"):
|
|
response = mesh_gate_mls.compose_encrypted_gate_message("finance-ops", "hello")
|
|
|
|
assert response == {"ok": False, "detail": "gate_mls_compose_failed"}
|
|
assert "finance-ops" not in caplog.text
|
|
assert "gate#" in caplog.text
|
|
|
|
|
|
def test_gate_persona_sign_logs_hashed_ids_on_failure(caplog, monkeypatch):
|
|
from services.mesh import mesh_wormhole_persona
|
|
|
|
monkeypatch.setattr(mesh_wormhole_persona, "bootstrap_wormhole_persona_state", lambda: None)
|
|
monkeypatch.setattr(
|
|
mesh_wormhole_persona,
|
|
"read_wormhole_persona_state",
|
|
lambda: {
|
|
"gate_personas": {
|
|
"finance": [
|
|
{
|
|
"persona_id": "persona-raw",
|
|
"private_key": "not-base64",
|
|
}
|
|
]
|
|
}
|
|
},
|
|
)
|
|
|
|
with caplog.at_level(logging.ERROR, logger="services.mesh.mesh_wormhole_persona"):
|
|
response = mesh_wormhole_persona.sign_gate_persona_blob("finance", "persona-raw", b"payload")
|
|
|
|
assert response == {"ok": False, "detail": "persona_blob_sign_failed"}
|
|
assert "persona-raw" not in caplog.text
|
|
assert "gate#" in caplog.text
|
|
assert "persona#" in caplog.text
|
|
|
|
|
|
def test_reputation_logs_hash_node_and_gate_identifiers(tmp_path, monkeypatch, caplog):
|
|
from services.mesh import mesh_reputation
|
|
|
|
monkeypatch.setattr(mesh_reputation, "DATA_DIR", tmp_path)
|
|
monkeypatch.setattr(mesh_reputation, "LEDGER_FILE", tmp_path / "reputation_ledger.json")
|
|
monkeypatch.setattr(mesh_reputation, "GATES_FILE", tmp_path / "gates.json")
|
|
|
|
ledger = mesh_reputation.ReputationLedger()
|
|
suffix = tmp_path.name.replace("-", "")
|
|
voter_id = f"!alpha-{suffix}"
|
|
target_id = f"!bravo-{suffix}"
|
|
gate_id = f"finance-{suffix}"
|
|
|
|
with caplog.at_level(logging.INFO, logger="services.mesh_reputation"):
|
|
ledger.register_node(voter_id, "pub-a", "Ed25519")
|
|
ledger.register_node(target_id, "pub-b", "Ed25519")
|
|
ok, _detail, _weight = ledger.cast_vote(voter_id, target_id, 1, gate_id)
|
|
|
|
assert ok is True
|
|
assert voter_id not in caplog.text
|
|
assert target_id not in caplog.text
|
|
assert gate_id not in caplog.text
|
|
assert "node#" in caplog.text
|
|
assert "gate#" in caplog.text
|