| .. | ||
| README.md | ||
| test_mcp_lifecycle.py | ||
| test_mcp_protocol_errors.py | ||
| test_oauth_configuration.py | ||
MCP security regression coverage
LIT-4506 tracks ten gateway guards and the later JWT/OAuth acceptance. This inventory distinguishes executable assertions from unresolved coverage. A listed test counts as verified only when its exact commit has an executed, passing result
Run the controlled gateway cases through python tests/integration/run.py extensions. They use real HTTP, PostgreSQL, scoped non-master keys and an SDK upstream. The existing runner supplies test entitlement; these tests do not validate licenses or external-provider consent. Canonical nodes and contract IDs live in ../contracts.json
| Requested guard | Existing or added coverage | Remaining limitation and owner |
|---|---|---|
| 1. Discovery scoped by org/team/user/key | test_mcp_lifecycle.py checks the exact key-granted catalog and health visibility in both management modes. PR #38680 adds team/org/user toolset E2E assertions |
Per-principal native MCP coverage is not established by REST results; reuse #38680 rather than duplicate it |
| 2. Users cannot attach unauthorized servers to their own keys | Existing live probes are recorded on LIT-4506; they are not durable endpoint regression tests | Own-key create/update escalation and its permission-validator boundary remain on existing management security tickets, including LIT-4502. A generic route denial does not prove that validator ran |
| 3. UI/API permission parity | Existing dashboard tests cover admin operations | The same non-admin actor must be tested through browser and API; admin UI tests do not establish parity. Retained with LIT-3644 |
| 4. Server ID determines identity | test_mcp_lifecycle.py grants one of two servers sharing a URL and denies calls to the other, using explicit server IDs for direct REST calls and server-qualified search results for virtual calls, with and without bearer credentials |
Virtual calls identify the target by the searched tool name, not the REST server_id field. Bare names such as add are ambiguous across servers; duplicate aliases/names and unprefixed protocol routing remain with LIT-4500 |
| 5. Same-URL servers do not share credentials | test_oauth_configuration.py crosses two gateway users with two server IDs and four distinct stored OAuth tokens. It checks actual upstream headers and successful results, then invalidates only one tuple |
Controlled stored-token tests do not prove separate external-provider accounts or consent flows |
| 6. OAuth never falls back to anonymous | OAuth isolation variants remove a stored token or expire it without refresh, require separate list/call auth failures and no upstream requests, and preserve all other valid tuples. test_mcp_lifecycle.py also covers warm static-header removal and OBO without a caller JWT |
External upstream revocation, refresh/reauthorization and aggregate challenges remain with LIT-4501, LIT-3433, LIT-4422 and LIT-4436 |
| 7. Stateful HTTP/session continuity | Legacy public-client tests exercise initialized sessions | No claim here proves upstream session state continuity; retained under LIT-3143 |
| 8. Production guardrails/hooks run | ../observability/test_guardrail_effects.py checks selected pre-call guards on direct and virtual execution, key/team/request selection, allowed results and zero denied executions. The two legacy test-owned dispatcher files are removed |
This does not establish every post-call/output-scanning or concurrent hook contract |
| 9. Permissions enforced at discovery and execution | Exact key catalog and virtual search results plus forbidden direct/virtual calls in test_mcp_lifecycle.py; existing ../compatibility/test_persisted_toolsets.py checks tool-level ceiling, denied sibling and allowed control |
All principal/transport combinations are not established; link #38680's evidence for its additional principal cases |
| 10. Stateless/stateful matrix | These controlled peers use stateless HTTP upstreams | Stateful combinations depend on LIT-3143 and shared conformance runs. Modern-agent/legacy-upstream interaction remains deferred; legacy passes do not establish modern conformance |
Additional JWT/OAuth acceptance
LIT-3467 / PR #41909 owns one shared real login/consent, immediate list/call and cold-restart implementation, with aggregate SSO and explicitly configured per-server JWT variants. Reuse that implementation and its protected login secret; do not create another browser bootstrap here. Credit its exact-commit evidence separately from these controlled credential tests
The two-user/two-server cases here create non-admin users and scoped API keys through management APIs. They store synthetic upstream OAuth credentials through the real credential endpoint and assert the actual bearer at the owned upstream. This deliberately isolates credential lookup, expiry and revocation from consent. No gateway API key may replace the expected upstream token
Gateway JWT precedence, invalid/expired gateway JWTs, inactive-user denial, and their MCP-specific interaction with isolated credential lookup remain unverified by these API-key cases. General JWT unit/API tests are useful existing coverage but do not substitute for those MCP outcomes. Real-provider auth failures should extend LIT-3467's settled helpers; its explicit-header case must not be described as an uninterrupted Authorization-only OAuth flow
PR #41718 / LIT-7737 owns dependency and public-client compatibility checks. This suite consumes the merged SDK2 API and keeps the existing dependency constraints. Its result must be reported independently of an installation-matrix pass