Information Disclosure / Improper Access Control (RBAC bypass on team resources in unified storage)
Description
Security fix: The commit adds 'teams' to the iam.grafana.app allowlist in the authzLimitedClient, ensuring RBAC checks are applied to team search/list/read in unified storage. Prior to this change, any resource not explicitly on the allowlist defaulted to allow-all, which allowed users to enumerate or view all teams in unified storage (specifically in dual-writer mode 4/5). This created an information disclosure vulnerability where unauthorized users could discover team names (and potentially associated metadata) they should not have access to. The fix ensures that team-related operations are evaluated by the real access control client, aligning teams with users, dashboards, and folders in RBAC. The production impact is mitigated by migrating teams to unified storage and by ensuring authorization is consistently enforced for teams as with other resources.
Proof of Concept
PoC (pre-fix behavior vs post-fix behavior):
Prerequisites:
- Grafana instance with unified storage enabled and dual-writer mode (mode 4/5).
- Teams exist: TeamA (accessible to user alice) and TeamB (not accessible to alice).
- Alice has limited/no read permission to TeamB but needs to test team discovery via API.
- Admin or a user with sufficient rights creates the two teams and assigns permissions accordingly.
1) Authenticate as alice and obtain a token:
curl -s -X POST -H 'Content-Type: application/json' -d '{"user":"alice","password":"<password>"}' https://grafana.example/api/login/oauth
(or use a session cookie/ Bearer token as your setup requires)
TOKEN_A="<alice_token>"
2) Before fix (vulnerable path): Alice lists teams via the RBAC-protected endpoint. Due to the missing allowlist entry for teams, the authorization check short-circuits to allow-all and returns all teams, including TeamB.
curl -s -H "Authorization: Bearer $TOKEN_A" https://grafana.example/api/teams/search?query=
# Expected (before fix): {"teams":[{"name":"TeamA"},{"name":"TeamB"},...]} regardless of alice's real permissions
3) After applying the fix (RBAC for teams in the allowlist): Alice should only see teams she is allowed to read (e.g., TeamA).
curl -s -H "Authorization: Bearer $TOKEN_A" https://grafana.example/api/teams/search?query=
# Expected (after fix): {"teams":[{"name":"TeamA"}]}
4) Optional negative test: With a user who has no team read permissions at all, the response should be empty or only include teams explicitly allowed for that user.
Commit Details
Author: Cory Forseth
Date: 2026-07-03 07:29 UTC
Message:
Unified storage: Enforce RBAC for team search/list/read (#127788)
fix(unified-storage): enforce RBAC for team search/list/read
The authzLimitedClient only enforces authorization for group/resource
pairs in its allowlist; anything unlisted short-circuits to allow-all in
Check/Compile/BatchCheck. iam.grafana.app/teams was missing, so once
teams read from unified storage (dual-writer mode 4/5) every user could
see every team via searchTeams/list/read regardless of teams:read.
Add teams to the iam.grafana.app allowlist so team authorization is
resolved by the real access client, matching users, dashboards, and
folders. Verified against a mode-5 environment: scoped users now see
only the teams they can read, wildcard users see all, filtered by the
storage layer before pagination.
Not currently reachable in production, which still reads teams from
legacy storage (SQL ac.Filter); this closes the gap for the in-progress
teams->unified migration.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Triage Assessment
Vulnerability Type: Information Disclosure
Confidence: HIGH
Reasoning:
The commit changes the authorization allowlist to include teams under iam.grafana.app, ensuring RBAC checks apply to team search/list/read in unified storage. Previously, unlisted resources defaulted to allow-all, allowing users to enumerate/view all teams. This directly fixes an authorization bypass and reduces information disclosure risk.
Verification Assessment
Vulnerability Type: Information Disclosure / Improper Access Control (RBAC bypass on team resources in unified storage)
Confidence: HIGH
Affected Versions: < 12.4.0
Code Diff
diff --git a/pkg/storage/unified/resource/access.go b/pkg/storage/unified/resource/access.go
index 2b13aef3b47d..38d4f392b31d 100644
--- a/pkg/storage/unified/resource/access.go
+++ b/pkg/storage/unified/resource/access.go
@@ -104,7 +104,7 @@ func NewAuthzLimitedClient(client claims.AccessClient, opts AuthzOptions) claims
allowlist: groupResource{
"dashboard.grafana.app": map[string]interface{}{"dashboards": nil},
"folder.grafana.app": map[string]interface{}{"folders": nil},
- "iam.grafana.app": map[string]interface{}{"users": nil},
+ "iam.grafana.app": map[string]interface{}{"users": nil, "teams": nil},
},
logger: logger,
metrics: newMetrics(opts.Registry),