Repository navigation
Description
What is wrong
At main (4c83b1d), prototype/gate_store.py and prototype/cmcp_gate.py have three security and fail-closed boundary bugs:
-
RemoteGateStorecrashes on non-UTF-8 binary secrets and accepts secrets shorter than 16 bytes:GateStoreServer.__init__(prototype/gate_store.py:337-343) acceptssecret: bytesand enforcesif len(secret) < 16: raise ValueError("store secret must be at least 16 bytes").RemoteGateStore.__init__(prototype/gate_store.py:381-384) takessecret: bytesand immediately callsself._secret = secret.decode(), which raisesUnicodeDecodeErroron any random 16+-byte binary key (os.urandom(32)), and does not enforceisinstance(secret, bytes)orlen(secret) >= 16.
-
RemoteGateStoreleaksValueError(binascii.Error),TypeError, andIndexErroron malformed server responses instead of raisingGateStoreUnavailable, and_unb64/_recvaccept non-canonical input:GateStoreUnavailable(prototype/gate_store.py:42-46) is deliberately aConnectionErrorand not aValueError, because "cMCP turns aValueErrorfrom the gate into a refusal receipt, which would need the same unreachable store."- However, in
RemoteGateStore.admission,bump_admission,receipt_head, andreceipts(prototype/gate_store.py:415-451), calling_unb64(found[0]),_int(...),_str(...), or unpackingexisting, head = self._call(...)on a malformed store response raisesbinascii.Error(aValueErrorsubclass!),TypeError, orValueErroroutside_call'stryblock, violating theGateStoreUnavailablecontract. gate_store._unb64(lines 262-265) accepts padded and non-canonical base64url strings instead of enforcing canonical unpadded base64url, and_recv(lines 281-285) does not reject duplicate JSON keys or catchRecursionError.
-
CMCPTraceGatedoes not validategateway_key,gateway_issuer,session_id,call_id, oractiontypes at entry:CMCPTraceGate.__init__(prototype/cmcp_gate.py:40-66) does not validateisinstance(context, v.VerificationContext),isinstance(gateway_key, Ed25519PrivateKey),gateway_issueras a validIdentifier, orcallable(clock).challenge,admit,begin,recheck, andrefusaldo not validatesession_id/call_idasIdentifierstrings orisinstance(action, dict)before hashingactionwithv.canonical_digest(action)orrfc8785.dumps(action)(which accepts lists, strings, or integers as top-level JSON values).
Reproduction on 4c83b1d:
from prototype import gate_store
# 1. Binary 16-byte secret raises UnicodeDecodeError:
gate_store.RemoteGateStore("127.0.0.1", 9999, b"\x80" * 16)
# 2. 5-byte secret is accepted by RemoteGateStore:
gate_store.RemoteGateStore("127.0.0.1", 9999, b"short")
# 3. Padded base64url is accepted by _unb64:
assert gate_store._unb64("AAAA====") == b"\x00\x00\x00"Spec section or file
prototype/gate_store.py(GateStoreServer,RemoteGateStore,_unb64,_recv)prototype/cmcp_gate.py(CMCPTraceGate)
Expected behavior
- Both
GateStoreServerandRemoteGateStorevalidateisinstance(secret, bytes)andlen(secret) >= 16and support arbitrary binarybytessecrets across the wire (or UTF-8 strings / base64url without crashing on binary bytes). gate_store._unb64enforces canonical unpadded base64url,_recvrejects duplicate JSON keys and catchesRecursionError, andRemoteGateStorewraps any malformed response structure/field error inGateStoreUnavailablesoValueError/TypeErrornever leaks into cMCP's refusal-receipt path.CMCPTraceGatevalidatescontext,gateway_key,gateway_issuer,clock,session_id,call_id, andisinstance(action, dict)withProfileError.
Impact
RemoteGateStore cannot be initialized with random binary secrets (os.urandom(32)), and a corrupted or truncated store response raises ValueError (binascii.Error) or TypeError instead of GateStoreUnavailable, causing cMCP to attempt writing a refusal receipt to the broken store instead of failing closed.
Activity
prototype(gate_store,cmcp_gate): harden store secrets, wire framing, …
prototype(gate_store,cmcp_gate): harden store secrets, wire framing, …
prototype(gate_store,cmcp_gate): harden store secrets, wire framing, …