diff --git a/.gitignore b/.gitignore index 73edcbb..e387de6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ htmlcov/ .coverage.* coverage.xml *.cover + +# Runtime artifacts +*.db +audit.db diff --git a/src/cmcp_runtime/mcp/server.py b/src/cmcp_runtime/mcp/server.py index febeb5e..3035f83 100644 --- a/src/cmcp_runtime/mcp/server.py +++ b/src/cmcp_runtime/mcp/server.py @@ -206,15 +206,20 @@ async def _handle_mcp(self, request: Request) -> Response: """Handle MCP JSON-RPC 2.0 calls.""" # DOS-001: reject oversized requests before parsing to prevent OOM content_length = request.headers.get("content-length") - if content_length and int(content_length) > self._max_request_bytes: - return JSONResponse( - { - "jsonrpc": "2.0", - "error": {"code": -32600, "message": "Request body too large"}, - "id": None, - }, - status_code=413, - ) + if content_length: + try: + cl = int(content_length) + except ValueError: + return JSONResponse({"error": "invalid Content-Length"}, status_code=400) + if cl > self._max_request_bytes: + return JSONResponse( + { + "jsonrpc": "2.0", + "error": {"code": -32600, "message": "Request body too large"}, + "id": None, + }, + status_code=413, + ) try: body = await request.body() if len(body) > self._max_request_bytes: diff --git a/src/cmcp_verify/opaque.py b/src/cmcp_verify/opaque.py index b7ae245..2fe1da4 100644 --- a/src/cmcp_verify/opaque.py +++ b/src/cmcp_verify/opaque.py @@ -71,6 +71,12 @@ def verify_opaque_measurement( result.details["hint"] = "raw_evidence not provided; cannot verify with Opaque" return result + if not endpoint or not endpoint.startswith("https://"): + raise ValueError( + f"Opaque attestation endpoint must use https://. Got: {endpoint!r}. " + "Set CMCP_OPAQUE_ATTESTATION_ENDPOINT to a valid https:// URL." + ) + # POST raw_evidence (base64-encoded) to the Opaque attestation endpoint payload = json.dumps({ "measurement": measurement, diff --git a/tests/unit/test_low_batch_186_187_191_194.py b/tests/unit/test_low_batch_186_187_191_194.py index 1f1235f..b382320 100644 --- a/tests/unit/test_low_batch_186_187_191_194.py +++ b/tests/unit/test_low_batch_186_187_191_194.py @@ -184,7 +184,7 @@ def test_redact_auth_headers_no_auth_unchanged(): def test_opaque_api_key_not_logged_on_failure(monkeypatch, caplog): """HW-008: OPAQUE_API_KEY value must not appear in log output on failure.""" - monkeypatch.setenv("CMCP_OPAQUE_ATTESTATION_ENDPOINT", "https://attest.opaque.co/v1/verify") + monkeypatch.setenv("CMCP_OPAQUE_ATTESTATION_ENDPOINT", "https://attest.example.com/v1/verify") monkeypatch.setenv("OPAQUE_API_KEY", "sk-supersecret-key-do-not-log") import cmcp_verify.opaque as opaque_mod importlib.reload(opaque_mod) @@ -211,7 +211,7 @@ def mock_urlopen(req, timeout=None): opaque_mod.verify_opaque_measurement( "sha384:" + "a" * 96, b"\x00" * 64, - opaque_endpoint="https://attest.opaque.co/v1/verify", + opaque_endpoint="https://attest.example.com/v1/verify", ) auth = captured.get("headers", {}).get("authorization") assert auth == "Bearer test-api-key-12345" diff --git a/tests/unit/test_tdx_opaque_verify.py b/tests/unit/test_tdx_opaque_verify.py index c197e28..fb27551 100644 --- a/tests/unit/test_tdx_opaque_verify.py +++ b/tests/unit/test_tdx_opaque_verify.py @@ -102,7 +102,7 @@ def test_opaque_no_endpoint_configured(monkeypatch): def test_opaque_no_raw_evidence_fails_closed(monkeypatch): - monkeypatch.setenv("CMCP_OPAQUE_ATTESTATION_ENDPOINT", "https://attest.opaque.co/v1/verify") + monkeypatch.setenv("CMCP_OPAQUE_ATTESTATION_ENDPOINT", "https://attest.example.com/v1/verify") result = verify_opaque_measurement("sha384:" + "a" * 96, None) assert result.verified is False assert result.failure_reason == "no_raw_evidence" @@ -121,7 +121,7 @@ def test_opaque_endpoint_returns_verified(monkeypatch): result = verify_opaque_measurement( "sha384:" + "a" * 96, b"\x00" * 64, - opaque_endpoint="https://attest.opaque.co/v1/verify", + opaque_endpoint="https://attest.example.com/v1/verify", ) assert result.verified @@ -140,7 +140,7 @@ def test_opaque_endpoint_returns_unverified(monkeypatch): result = verify_opaque_measurement( "sha384:" + "a" * 96, b"\x00" * 64, - opaque_endpoint="https://attest.opaque.co/v1/verify", + opaque_endpoint="https://attest.example.com/v1/verify", ) assert not result.verified @@ -154,7 +154,7 @@ def test_opaque_network_error(monkeypatch): result = verify_opaque_measurement( "sha384:" + "a" * 96, b"\x00" * 64, - opaque_endpoint="https://attest.opaque.co/v1/verify", + opaque_endpoint="https://attest.example.com/v1/verify", ) assert result.verified