From 756797c2c4ec162db4e1aab7919d67fc1a45e914 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Wed, 28 Oct 2026 16:10:03 +0100 Subject: [PATCH] test(32.1-05): keep implied MCP scopes off the subject - MCPDispatch expands the credential and leaves subject.Scopes unchanged --- mcp_implies_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mcp_implies_test.go b/mcp_implies_test.go index a2925c2..4e8ebf0 100644 --- a/mcp_implies_test.go +++ b/mcp_implies_test.go @@ -154,6 +154,30 @@ func TestMCPImpliesWriteDoesNotGrantRead(t *testing.T) { } } +// TestMCPImpliesNotPersisted pins that MCPDispatch expands scopes only on +// the credential it returns. The subject's grant slice is the same backing +// array afterwards. +func TestMCPImpliesNotPersisted(t *testing.T) { + _, guard := impliesApp(t, impliesServersRead()) + ctx := context.Background() + subject := pact.MCPSubject{Guard: "user", UserID: 7, ClientID: "oauth-client", Scopes: []string{"read"}} + orig := subject.Scopes + dispatch, err := guard.MCPDispatch(ctx, subject, "GET") + if err != nil { + t.Fatal(err) + } + credential, isToken := dispatch.Credential.(*models.APIToken) + if !isToken || !credential.HasScope("servers:read") || !credential.HasScope("read") { + t.Fatalf("credential = %+v, want the expanded scopes on the credential only", dispatch.Credential) + } + if len(subject.Scopes) != 1 || &subject.Scopes[0] != &orig[0] || subject.Scopes[0] != "read" { + t.Fatalf("subject.Scopes = %v, want the original grant slice", subject.Scopes) + } + if slices.Contains(subject.Scopes, "servers:read") { + t.Fatal("implied name was written onto subject.Scopes") + } +} + // TestMCPImpliesAlreadyPresent does not append a declared name the grant // already holds, and does not append a catalog name whose Implies is empty. func TestMCPImpliesAlreadyPresent(t *testing.T) {