test(broker): direct cross-user isolation test for CredentialResolver (MCP-2578)#690
Conversation
…lver Backlog follow-up to MCP-1039 (#688), which verified user isolation only structurally (every lookup is store.Get(userID, serverKey)-keyed with no shared fallback). This adds an end-to-end behavioural guard: seed user B's credential and assert Resolve as user A for the same serverKey falls closed (token-exchange error / NotConnectedError) and never returns user B's token. The seeded user B credential is valid and not near expiry, so a regression dropping the per-user keying would make acquire() return it directly from cache — the test fails in exactly that case (verified by simulating a shared fallback). Covers both token_exchange and oauth_connect modes. Related #688
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 27558046516 --repo smart-mcp-proxy/mcpproxy-go
|
|
Code Review — CEO Agent (MCP-2582) Test-only PR. Verified locally: Assessment: ACCEPT Test design is correct and the regression guard is well-constructed:
No production code touched. Ready to merge. |
Deploying mcpproxy-docs with
|
| Latest commit: |
7846879
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://32f4f0d4.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://test-mcp-2578-cred-resolver.mcpproxy-docs.pages.dev |
There was a problem hiding this comment.
Claude review ACCEPT (codex out). Direct cross-user isolation test for CredentialResolver: seeds userB cred (valid + refresh-token variants), resolves as userA, asserts userA gets fail-closed (nil + NotConnectedError/err) and the tight isolation checks ex.calls==1 / refreshCalls==0 / error never contains userBToken — would genuinely fail if keying leaked. -tags server -race passes. Closes the #688 follow-up gap (MCP-2578).
|
CEO Code Review — LGTM (CodexReviewer exhausted credits; reviewed as MCP-2587 backstop) Two subtests, both PASS (
Review notes:
Ready to merge. |
Summary
Backlog follow-up to MCP-1039 / #688 (merged). #688 verified per-user isolation only structurally — every lookup is
store.Get(userID, serverKey)-keyed with no shared fallback — but there was no explicit test seeding user B's credential and asserting user A'sResolvenever returns it.This PR adds
TestResolve_CrossUserIsolation_NeverReturnsAnotherUsersCredentialininternal/serveredition/broker(build tagserver):Resolveas userA for the same serverKey.got == nil, and userA goes through its own acquisition (1Exchangecall, proving no cache short-circuit to userB).*NotConnectedErrorwith userA's connect URL, no refresh against userB's cached credential, error never contains userB's token.Why it's a real regression guard
The seeded userB credential is valid and not near expiry, so a regression dropping per-user keying (a shared/static fallback) would make
acquire()return it directly from cache. Verified by temporarily simulating a shared fallback — both sub-tests fail and userA literally receivesuserB-secret-token-MUST-NOT-LEAK.Verification
go test -tags server ./internal/serveredition/broker/... -race→ greengolangci-lint(CI v2 config,--build-tags server) → 0 issuesRelated #688