Authorization

MEDIUM
grafana/grafana
Commit: 2c96c4a6033f
Affected: <=12.4.0
2026-06-09 15:02 UTC

Description

The commit fixes the IAM datasource permission mapping to Kubernetes-style scopes, explicitly handling wildcards and missing datasource types when converting legacy scopes to the Kubernetes form. This addresses potential authorization gaps where incorrect or inconsistent mapping could grant or deny access in unintended ways, particularly for wildcard scopes (datasources:*) and missing datasource types. The change reduces the risk of privilege escalation or improper access control arising from misinterpreted legacy IAM scopes.

Proof of Concept

PoC (conceptual, actionable steps): Goal: Demonstrate how wildcard datasource permissions should map to Kubernetes-style scopes and actions, and what could go wrong without the fix. 1) Before the fix (illustrative, theoretical): - Given inputs: datasourceType = "" (empty), scope = "datasources:*", action = "datasources.permissions:write". - Expected (pre-fix) behavior may misinterpret the wildcard due to missing type handling, potentially resulting in an invalid or overly broad Kubernetes resource path or an incorrect action mapping. - This could, in a real IAM evaluation, allow a user to perform write/permissions actions across datasources without a properly scoped resource path. Example (illustrative Go-like pseudocode): dsType := "" scope := "datasources:*" action := "datasources.permissions:write" newScope, newAction := LegacyDatasourceScopeAndActionToK8s(dsType, scope, action) fmt.Println(newScope, newAction) Expected outcome (pre-fix) unclear or potentially unsafe due to missing ds type handling. 2) After the fix (as implemented in this commit): - Given inputs: datasourceType = "*", scope = "datasources:*", action = "datasources.permissions:write". - The function now detects a wildcard uid and sets datasourceType = "*" accordingly, then maps to a concrete Kubernetes resource path and action: - Expected mapped scope: "*.datasource.grafana.app/datasources:*" - Expected mapped action: "set_permissions" Example (Go-like pseudocode): dsType := "*" scope := "datasources:*" action := "datasources.permissions:write" newScope, newAction := LegacyDatasourceScopeAndActionToK8s(dsType, scope, action) fmt.Println(newScope, newAction) // -> "*.datasource.grafana.app/datasources:*" and "set_permissions" 3) How to reproduce in a test environment: - Load Grafana with this commit or a fork that includes the fix. - Call the IAM mapping function with inputs above and verify the output matches the expected post-fix values. - Attempt to enforce a permissions-grant operation using a wildcard scope and confirm that the mapping applies to the correct Kubernetes-form resource path and action, and that the authorization decision is correctly constrained. Impact: The PoC demonstrates that the fix aligns wildcard and missing datasource-type scenarios with explicit Kubernetes resource paths, reducing the risk of unintended permission grants or bypasses due to incorrect scope translation.

Commit Details

Author: Gabriel MABILLE

Date: 2026-06-09 14:00 UTC

Message:

`iam`: Correctly map datasource permissions (#126044) * `iam`: Correctly map datasource permissions * Reapply suggestion Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>

Triage Assessment

Vulnerability Type: Authorization

Confidence: MEDIUM

Reasoning:

The commit adjusts how IAM datasource permissions are mapped from legacy scopes to Kubernetes form, including handling of wildcards and missing datasource types. This directly affects authorization decisions and could prevent incorrect permission grants or bypasses by ensuring permissions are mapped consistently, addressing potential access control/security gaps.

Verification Assessment

Vulnerability Type: Authorization

Confidence: MEDIUM

Affected Versions: <=12.4.0

Code Diff

diff --git a/pkg/registry/apis/iam/datasourcek8s/k8s.go b/pkg/registry/apis/iam/datasourcek8s/k8s.go index ec5aaac6c8330..c633c58d9e689 100644 --- a/pkg/registry/apis/iam/datasourcek8s/k8s.go +++ b/pkg/registry/apis/iam/datasourcek8s/k8s.go @@ -1,6 +1,10 @@ package datasourcek8s -import "strings" +import ( + "strings" + + "github.com/grafana/grafana/pkg/services/accesscontrol" +) // K8sDatasourceAPIGroup returns the k8s API group for a Grafana datasource type func K8sDatasourceAPIGroup(dsType string) string { @@ -72,12 +76,19 @@ func LegacyDatasourceAction(dsType string, action *string) { // LegacyDatasourceScopeAndActionToK8s converts legacy datasource scope and action to k8s form func LegacyDatasourceScopeAndActionToK8s(datasourceType, scope, action string) (string, string) { - if datasourceType == "" { + kind, _, uid := accesscontrol.SplitScope(scope) + if kind != "datasources" { return scope, action } - if uid, ok := strings.CutPrefix(scope, "datasources:uid:"); ok { - scope = LegacyUIDScopeToK8s(datasourceType, uid) + if uid == "*" { + // datasource type is not set for wildcard scopes, we use "*" instead + datasourceType = "*" + } else if datasourceType == "" { + // TODO: datasource type is not set, this should be an error + return scope, action } + + scope = LegacyUIDScopeToK8s(datasourceType, uid) if converted, ok := legacyActionToK8s(datasourceType, action); ok { action = converted } diff --git a/pkg/registry/apis/iam/datasourcek8s/k8s_test.go b/pkg/registry/apis/iam/datasourcek8s/k8s_test.go index d6b1acb089d57..f253d23dc1dda 100644 --- a/pkg/registry/apis/iam/datasourcek8s/k8s_test.go +++ b/pkg/registry/apis/iam/datasourcek8s/k8s_test.go @@ -38,6 +38,10 @@ func TestLegacyDatasourceScopeAndActionToK8s(t *testing.T) { scope, action = LegacyDatasourceScopeAndActionToK8s("loki", "datasources:uid:abc", "datasources:read") assert.Equal(t, "loki.datasource.grafana.app/datasources:abc", scope) assert.Equal(t, "loki.datasource.grafana.app/datasources:get", action) + + scope, action = LegacyDatasourceScopeAndActionToK8s("*", "datasources:*", "datasources.permissions:write") + assert.Equal(t, "*.datasource.grafana.app/datasources:*", scope) + assert.Equal(t, "*.datasource.grafana.app/datasources:set_permissions", action) } func TestLegacyDatasourceAction(t *testing.T) {
← Back to Alerts View on GitHub →