Harden infonet control surfaces

This commit is contained in:
BigBodyCobain
2026-05-18 11:22:38 -06:00
parent 25a98a9869
commit 11ea345518
30 changed files with 1810 additions and 276 deletions
+57 -1
View File
@@ -5,7 +5,7 @@ from starlette.requests import Request
from starlette.responses import Response
def _request(path: str, method: str = "POST") -> Request:
def _request(path: str, method: str = "POST", query_string: bytes = b"") -> Request:
return Request(
{
"type": "http",
@@ -13,6 +13,7 @@ def _request(path: str, method: str = "POST") -> Request:
"client": ("test", 12345),
"method": method,
"path": path,
"query_string": query_string,
}
)
@@ -504,6 +505,61 @@ def test_private_infonet_gate_write_returns_preparing_state_when_wormhole_not_re
get_settings.cache_clear()
def test_invite_scoped_prekey_lookup_reaches_handler_while_lane_prepares(monkeypatch):
"""Copied-address import must not be blocked by private-lane warmup."""
import main
import auth
from services.config import get_settings
from services import wormhole_supervisor
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")
get_settings.cache_clear()
monkeypatch.setattr(
auth,
"_anonymous_mode_state",
lambda: {
"enabled": False,
"wormhole_enabled": True,
"ready": False,
"effective_transport": "direct",
},
)
monkeypatch.setattr(
wormhole_supervisor,
"get_wormhole_state",
lambda: {
"configured": True,
"ready": False,
"rns_ready": False,
"arti_ready": False,
},
)
called = {"value": False}
async def call_next(_request: Request) -> Response:
called["value"] = True
return Response(status_code=200)
response = asyncio.run(
main.enforce_high_privacy_mesh(
_request(
"/api/mesh/dm/prekey-bundle",
method="GET",
query_string=b"lookup_token=invite-handle",
),
call_next,
)
)
assert response.status_code == 200
assert called["value"] is True
get_settings.cache_clear()
def test_private_dm_send_blocks_at_transitional_tier(monkeypatch):
import main
import auth
+14 -3
View File
@@ -47,6 +47,11 @@ def test_infonet_ingest_accepts_valid_event(tmp_path, monkeypatch):
assert result["accepted"] == 1
assert inf.head_hash == evt.event_id
info = inf.get_info()
assert info["known_nodes"] == 1
assert info["author_nodes"] == 1
assert info["total_events"] == 1
assert info["event_types"]["message"] == 1
def test_verify_node_binding_accepts_current_and_compat_ids_only(monkeypatch):
@@ -64,6 +69,8 @@ def test_verify_node_binding_accepts_current_and_compat_ids_only(monkeypatch):
f"{current[len(mesh_crypto.NODE_ID_PREFIX):len(mesh_crypto.NODE_ID_PREFIX) + 8]}"
)
monkeypatch.setenv("MESH_DEV_ALLOW_LEGACY_COMPAT", "true")
monkeypatch.setenv("MESH_BLOCK_LEGACY_NODE_ID_COMPAT", "false")
monkeypatch.setenv("MESH_ALLOW_LEGACY_NODE_ID_COMPAT_UNTIL", "2099-01-01")
from services.config import get_settings
@@ -98,7 +105,7 @@ def test_infonet_append_rejects_missing_signature_fields(tmp_path, monkeypatch):
assert "signature" in str(exc).lower()
def test_infonet_load_fails_closed_on_hash_mismatch(tmp_path, monkeypatch):
def test_infonet_load_quarantines_and_resets_on_hash_mismatch(tmp_path, monkeypatch):
monkeypatch.setattr(mesh_hashchain, "DATA_DIR", tmp_path)
monkeypatch.setattr(mesh_hashchain, "CHAIN_FILE", tmp_path / "infonet.json")
@@ -135,8 +142,12 @@ def test_infonet_load_fails_closed_on_hash_mismatch(tmp_path, monkeypatch):
encoding="utf-8",
)
with pytest.raises(ValueError, match="Hash mismatch on event load"):
mesh_hashchain.Infonet()
inf = mesh_hashchain.Infonet()
assert inf.events == []
assert inf.head_hash == mesh_hashchain.GENESIS_HASH
assert not mesh_hashchain.CHAIN_FILE.exists()
assert list(tmp_path.glob("infonet.json.quarantine.*"))
def test_validate_gate_message_payload_rejects_plaintext_shape():
@@ -12,6 +12,7 @@ Tests verify:
"""
import hashlib
import json
import time
from services.config import get_settings
@@ -611,6 +612,99 @@ class TestFetchPrekeyBundleByLookup:
"peer prekey lookup unavailable",
}
def test_fetch_lookup_token_uses_bootstrap_peer_without_agent_id(self, tmp_path, monkeypatch):
"""Invite lookup can resolve through bootstrap peers without exposing agent_id."""
_isolated_relay(tmp_path, monkeypatch)
record = _valid_bundle_record("test-agent")
requested_urls: list[str] = []
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "https://seed.example")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
monkeypatch.setenv("MESH_RELAY_PEERS", "")
get_settings.cache_clear()
class _Response:
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, _limit: int = -1):
return json.dumps(
{
"ok": True,
"identity_dh_pub_key": record["dh_pub_key"],
"dh_algo": record["dh_algo"],
"public_key": record["public_key"],
"public_key_algo": record["public_key_algo"],
"protocol_version": record["protocol_version"],
"sequence": 1,
"signed_at": int(record["bundle"].get("signed_at", 0) or 0),
"bundle": record["bundle"],
}
).encode("utf-8")
def _urlopen(request, timeout=0):
requested_urls.append(str(getattr(request, "full_url", "")))
return _Response()
monkeypatch.setattr("services.mesh.mesh_wormhole_prekey.urllib.request.urlopen", _urlopen)
from services.mesh.mesh_wormhole_prekey import fetch_dm_prekey_bundle
result = fetch_dm_prekey_bundle(agent_id="", lookup_token="bootstrap-handle")
assert result["ok"] is True
assert result["agent_id"] == record["agent_id"]
assert result["lookup_mode"] == "invite_lookup_handle"
assert result["public_lookup"] is True
assert requested_urls
assert "lookup_token=bootstrap-handle" in requested_urls[0]
assert "agent_id" not in requested_urls[0]
def test_fetch_lookup_token_does_not_parse_peer_pending_as_bundle(self, tmp_path, monkeypatch):
"""A peer's private-lane pending response is not a malformed prekey bundle."""
_isolated_relay(tmp_path, monkeypatch)
requested_urls: list[str] = []
monkeypatch.setenv("MESH_BOOTSTRAP_SEED_PEERS", "https://seed.example")
monkeypatch.setenv("MESH_DEFAULT_SYNC_PEERS", "")
monkeypatch.setenv("MESH_RELAY_PEERS", "")
get_settings.cache_clear()
class _Response:
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, _limit: int = -1):
return json.dumps(
{
"ok": True,
"pending": True,
"status": "preparing_private_lane",
"detail": "transport tier insufficient",
}
).encode("utf-8")
def _urlopen(request, timeout=0):
requested_urls.append(str(getattr(request, "full_url", "")))
return _Response()
monkeypatch.setattr("services.mesh.mesh_wormhole_prekey.urllib.request.urlopen", _urlopen)
from services.mesh.mesh_wormhole_prekey import fetch_dm_prekey_bundle
result = fetch_dm_prekey_bundle(agent_id="", lookup_token="bootstrap-handle")
assert requested_urls
assert result["ok"] is False
assert result["detail"] == "peer prekey lookup still preparing"
assert result["detail"] != "Prekey bundle missing signing key"
def test_fetch_agent_id_uses_pinned_contact_lookup_handle(self, tmp_path, monkeypatch):
"""Pinned invite lookup handle is used before direct agent_id lookup."""
relay = _isolated_relay(tmp_path, monkeypatch)
@@ -71,6 +71,38 @@ def _fresh_wormhole_state(tmp_path, monkeypatch):
return relay, mesh_wormhole_identity, mesh_wormhole_contacts, mesh_wormhole_prekey
def test_register_wormhole_dm_key_repairs_missing_local_dh_material(tmp_path, monkeypatch):
relay, identity_mod, _contacts_mod, _prekey_mod = _fresh_wormhole_state(tmp_path, monkeypatch)
identity = identity_mod.read_wormhole_identity()
original_node_id = identity["node_id"]
original_public_key = identity["public_key"]
original_private_key = identity["private_key"]
identity_mod.write_dm_identity(
{
**identity,
"dh_pub_key": "",
"dh_private_key": "",
"bundle_fingerprint": "",
"bundle_sequence": 0,
"bundle_registered_at": 0,
}
)
registered = identity_mod.register_wormhole_dm_key()
repaired = identity_mod.read_wormhole_identity()
assert registered["ok"] is True
assert registered["dh_pub_key"]
assert registered["dh_algo"] == "X25519"
assert repaired["dh_pub_key"] == registered["dh_pub_key"]
assert repaired["dh_private_key"]
assert repaired["node_id"] == original_node_id
assert repaired["public_key"] == original_public_key
assert repaired["private_key"] == original_private_key
assert relay.get_dh_key(original_node_id)["dh_pub_key"] == registered["dh_pub_key"]
def _export_verified_invite(identity_mod):
exported = identity_mod.export_wormhole_dm_invite()
assert exported["ok"] is True
@@ -460,6 +492,30 @@ def test_imported_dm_invite_pins_contact_as_invite_pinned(tmp_path, monkeypatch)
assert contacts_mod.list_wormhole_dm_contacts()[imported["peer_id"]]["trust_level"] == "invite_pinned"
def test_imported_dm_invite_saves_pending_contact_when_prekey_not_visible(tmp_path, monkeypatch):
_relay, identity_mod, contacts_mod, prekey_mod = _fresh_wormhole_state(tmp_path, monkeypatch)
exported, verified = _export_verified_invite(identity_mod)
monkeypatch.setattr(
prekey_mod,
"fetch_dm_prekey_bundle",
lambda **_kw: {"ok": False, "detail": "Prekey bundle not found"},
)
imported = identity_mod.import_wormhole_dm_invite(exported["invite"], alias="alice")
contact = imported["contact"]
assert imported["ok"] is True
assert imported["pending_prekey"] is True
assert imported["peer_id"] == verified["peer_id"]
assert contact["alias"] == "alice"
assert contact["trust_level"] == "invite_pinned"
assert contact["invitePinnedPrekeyLookupHandle"] == exported["invite"]["payload"]["prekey_lookup_handle"]
assert contact["remotePrekeyLookupMode"] == "invite_lookup_handle"
assert contact["remotePrekeyFingerprint"] == verified["trust_fingerprint"]
assert contact["dhPubKey"] == ""
assert contacts_mod.list_wormhole_dm_contacts()[verified["peer_id"]]["trust_level"] == "invite_pinned"
def test_imported_dm_invite_requires_root_attested_prekey_bundle(tmp_path, monkeypatch):
relay, identity_mod, _contacts_mod, _prekey_mod = _fresh_wormhole_state(tmp_path, monkeypatch)
@@ -0,0 +1,69 @@
"""Regression coverage for operator-only control surfaces."""
import pytest
@pytest.mark.parametrize(
("method", "path", "payload"),
[
("get", "/api/wormhole/identity", None),
("post", "/api/wormhole/identity/bootstrap", {}),
("post", "/api/wormhole/gate/enter", {"gate_id": "general-talk"}),
("post", "/api/wormhole/gate/leave", {"gate_id": "general-talk"}),
("post", "/api/wormhole/sign", {"event_type": "gate_event", "payload": {"ok": True}}),
("post", "/api/wormhole/gate/key/rotate", {"gate_id": "general-talk", "reason": "test"}),
(
"post",
"/api/wormhole/gate/key/grant",
{
"gate_id": "general-talk",
"recipient_node_id": "node-test",
"recipient_dh_pub": "dh-test",
},
),
("post", "/api/wormhole/gate/persona/create", {"gate_id": "general-talk", "label": "test"}),
(
"post",
"/api/wormhole/gate/persona/activate",
{"gate_id": "general-talk", "persona_id": "persona-test"},
),
("post", "/api/wormhole/gate/persona/clear", {"gate_id": "general-talk"}),
(
"post",
"/api/wormhole/gate/persona/retire",
{"gate_id": "general-talk", "persona_id": "persona-test"},
),
(
"post",
"/api/wormhole/gate/message/sign-encrypted",
{
"gate_id": "general-talk",
"epoch": 1,
"ciphertext": "ciphertext",
"nonce": "nonce",
"format": "mls1",
"envelope_hash": "hash",
},
),
("post", "/api/wormhole/gate/message/compose", {"gate_id": "general-talk", "plaintext": "hello"}),
("post", "/api/wormhole/sign-raw", {"message": "raw"}),
("post", "/api/wormhole/gate/state/export", {"gate_id": "general-talk"}),
("post", "/api/wormhole/gate/proof", {"gate_id": "general-talk"}),
("post", "/api/wormhole/connect", {}),
("post", "/api/layers", {"layers": {"viirs_nightlights": True}}),
("post", "/api/ais/feed", {"msgs": []}),
],
)
def test_remote_control_surface_rejects_without_local_operator_or_admin(
remote_client, method, path, payload
):
request = getattr(remote_client, method)
response = request(path, json=payload) if payload is not None else request(path)
assert response.status_code == 403
def test_remote_agent_actions_poll_rejects_without_local_operator_or_admin(remote_client):
response = remote_client.get("/api/ai/agent-actions")
assert response.status_code == 403
+52
View File
@@ -0,0 +1,52 @@
"""CrowdThreat ingestion is operator opt-in only."""
class _CrowdThreatResponse:
status_code = 200
def json(self):
return {
"data": {
"threats": [
{
"id": "ct-1",
"title": "Example report",
"location": {
"lng_lat": [12.5, 41.9],
"name": "Example place",
"country": {"name": "Italy"},
},
"category": {"id": 1, "name": "Security"},
}
]
}
}
def test_crowdthreat_disabled_by_default_does_not_call_upstream(monkeypatch):
from services.fetchers import _store, crowdthreat
monkeypatch.delenv("CROWDTHREAT_ENABLED", raising=False)
monkeypatch.setitem(_store.latest_data, "crowdthreat", [{"id": "old"}])
monkeypatch.setattr(
crowdthreat,
"fetch_with_curl",
lambda *args, **kwargs: (_ for _ in ()).throw(AssertionError("upstream called")),
)
crowdthreat.fetch_crowdthreat()
assert _store.latest_data["crowdthreat"] == []
def test_crowdthreat_opt_in_fetches_when_layer_is_enabled(monkeypatch):
from services.fetchers import _store, crowdthreat
monkeypatch.setenv("CROWDTHREAT_ENABLED", "true")
monkeypatch.setitem(_store.active_layers, "crowdthreat", True)
monkeypatch.setattr(crowdthreat, "fetch_with_curl", lambda *args, **kwargs: _CrowdThreatResponse())
crowdthreat.fetch_crowdthreat()
assert _store.latest_data["crowdthreat"][0]["id"] == "ct-1"
assert _store.latest_data["crowdthreat"][0]["source"] == "CrowdThreat"