M13: harden MCP trust boundary
This commit is contained in:
@@ -75,7 +75,7 @@ def test_demo_reset_preserves_integration_telemetry(ops_client, client):
|
||||
"/api/v1/integrations/mcp/operations-summary",
|
||||
headers={
|
||||
"X-Service-Token": settings.mcp_hub_service_token,
|
||||
"X-Client-Id": "reset-probe",
|
||||
"X-Client-Id": "itworx-mcp-hub:mobilityops:reset-probe",
|
||||
},
|
||||
)
|
||||
assert probe.status_code == 200
|
||||
@@ -83,4 +83,4 @@ def test_demo_reset_preserves_integration_telemetry(ops_client, client):
|
||||
|
||||
assert client.post("/api/v1/demo/login", json={"role": "operations_manager"}).status_code == 200
|
||||
events = client.get("/api/v1/audit", params={"action": "mcp_tool_request"}).json()
|
||||
assert any(event["actor_label"] == "reset-probe" for event in events)
|
||||
assert any(event["actor_label"].endswith(":reset-probe") for event in events)
|
||||
|
||||
@@ -5,7 +5,7 @@ def _headers(token: str | None = None, client_id: str = "test-mcp-client"):
|
||||
settings = get_settings()
|
||||
return {
|
||||
"X-Service-Token": token if token is not None else settings.mcp_hub_service_token,
|
||||
"X-Client-Id": client_id,
|
||||
"X-Client-Id": f"itworx-mcp-hub:mobilityops:{client_id}",
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,31 @@ def test_operations_summary_requires_service_token(client):
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_operations_summary_rejects_spoofed_client_identity(client):
|
||||
settings = get_settings()
|
||||
response = client.get(
|
||||
"/api/v1/integrations/mcp/operations-summary",
|
||||
headers={"X-Service-Token": settings.mcp_hub_service_token, "X-Client-Id": "spoofed"},
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_operations_summary_rejects_cross_tenant_request(client):
|
||||
response = client.get(
|
||||
"/api/v1/integrations/mcp/operations-summary",
|
||||
headers={**_headers(), "X-Tenant-Id": "another-tenant"},
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_operations_summary_returns_metrics(client):
|
||||
response = client.get("/api/v1/integrations/mcp/operations-summary", headers=_headers())
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert "metrics" in body
|
||||
assert body["metrics"]["available"] >= 0
|
||||
assert response.headers["x-correlation-id"]
|
||||
assert response.headers["x-tenant-id"] == get_settings().ragcore_tenant
|
||||
|
||||
|
||||
def test_attention_vehicles_filters_by_severity(client):
|
||||
@@ -72,6 +91,8 @@ def test_search_knowledge_grounded_and_respects_max_sources(client):
|
||||
body = response.json()
|
||||
assert body["evidence_state"] == "grounded"
|
||||
assert len(body["sources"]) == 1
|
||||
assert int(response.headers["x-sources-available"]) >= 1
|
||||
assert response.headers["x-sources-returned"] == "1"
|
||||
|
||||
|
||||
def test_mcp_tool_requests_are_audited(client, ops_client):
|
||||
@@ -118,7 +139,7 @@ def test_operations_summary_mints_correlation_id_when_none_supplied(client, ops_
|
||||
)
|
||||
assert response.status_code == 200
|
||||
events = ops_client.get("/api/v1/audit", params={"action": "mcp_tool_request"}).json()
|
||||
matching = [e for e in events if e["actor_label"] == "no-correlation-probe"]
|
||||
matching = [e for e in events if e["actor_label"].endswith(":no-correlation-probe")]
|
||||
assert len(matching) >= 1
|
||||
assert matching[0]["correlation_id"] # a fresh UUID was minted, not left empty
|
||||
|
||||
|
||||
Reference in New Issue
Block a user