From a5ce43f211239428a64e5bc2f55d0efd962498df Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Fri, 23 Oct 2026 20:29:30 +0200 Subject: [PATCH] fix(19.7): RB-WR-02 a padded token scope name fails boot validatePluginTokenScope checked the trimmed name with pact.ValidTokenScopeName while compileTokenScopes registered the untrimmed one, so " blog:read" booted, was listed under its padded name and could never be granted, and the duplicate check depended on registration order. The name is now validated exactly as registered, which is the shared rule pact.ValidTokenScopeName states. --- modules/cabana/token_scope_name_test.go | 8 +++++++- modules/cabana/token_scopes.go | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/cabana/token_scope_name_test.go b/modules/cabana/token_scope_name_test.go index ffada275..517da588 100644 --- a/modules/cabana/token_scope_name_test.go +++ b/modules/cabana/token_scope_name_test.go @@ -9,7 +9,8 @@ import ( // TestTokenScopeNameParity pins plan 07's shared scope name rule (P-30): // cabana's plugin scope validation accepts and refuses exactly the names // pact.ValidTokenScopeName does (the table of pact's TestFrontendTokenScopes; -// "*" is refused by both, by cabana also as reserved). +// "*" is refused by both, by cabana also as reserved). A padded name is +// refused by both: cabana validates the name it registers, untrimmed. func TestTokenScopeNameParity(t *testing.T) { names := []struct { name string @@ -25,6 +26,11 @@ func TestTokenScopeNameParity(t *testing.T) { {"a:b:c", false}, {"*", false}, {"with space", false}, + // RB-WR-02: a padded name is refused as registered, not trimmed first. + {" read", false}, + {"read ", false}, + {"read\n", false}, + {"\tblog:read", false}, } for _, c := range names { if got := pact.ValidTokenScopeName(c.name); got != c.ok { diff --git a/modules/cabana/token_scopes.go b/modules/cabana/token_scopes.go index 38bd1d8e..0f1f7554 100644 --- a/modules/cabana/token_scopes.go +++ b/modules/cabana/token_scopes.go @@ -103,8 +103,11 @@ func compileTokenScopes(reg *Registry, plugins []party.Plugin) error { } func validatePluginTokenScope(reg *Registry, pluginID string, scope pact.TokenScope, cat *tokenScopeCatalog) error { - name := strings.TrimSpace(scope.Name) - if name == "" || !pact.ValidTokenScopeName(name) { + // The name is checked exactly as compileTokenScopes registers it: a padded + // name (" blog:read", "blog:read\n") fails pact.ValidTokenScopeName and + // boot, instead of being listed under a name no token request can match. + name := scope.Name + if !pact.ValidTokenScopeName(name) { return fmt.Errorf("cabana: plugin %s registered invalid token scope name %q", pluginID, scope.Name) } if name == scopeAll || name == "admin" || strings.HasPrefix(name, "admin:") {