Skip to content

REPRO-2026-00226: Verified Repro With Script Download

REPRO-2026-00226: Grafana IAM LIST authorization could be bypassed for folder-scoped CRDs when a user had wildcard resource permissions, returning All: true without folder-level checks.

REPRO-2026-00226 is verified against grafana/grafana · Go. Affected versions: Versions prior to commit b9b897b3c512ee434341bb9d698eac24f90eca89 (folder-scoped LIST authz check occurs after wildcard scope check). Fixed in b9b897b3c512ee434341bb9d698eac24f90eca89. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00226.

REPRO-2026-00226 grafana/grafana · Go Jul 4, 2026 .txt
Severity
HIGH
Confidence
HIGH
Reproduced in
22m 1s
Tool calls
277
Spend
$10.29
01 · Overview

What Is REPRO-2026-00226?

REPRO-2026-00226 reproduces a high-severity broken authorization (CWE-863) in Grafana's IAM/AuthZ LIST path: for folder-scoped Kubernetes-native/custom resources, a user holding only a resource-type wildcard permission (scope: "*") could receive All: true from the AuthZ LIST API without any folder-level authorization check.

02 · Severity & CVSS

REPRO-2026-00226 Severity

REPRO-2026-00226 is rated high severity.

HIGH threat level
Weakness CWE-863 — Incorrect Authorization

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected grafana/grafana Versions

grafana/grafana · Go versions Versions prior to commit b9b897b3c512ee434341bb9d698eac24f90eca89 (folder-scoped LIST authz check occurs after wildcard scope check) are affected.

How to Reproduce REPRO-2026-00226

$ pruva-verify REPRO-2026-00226
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00226/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for REPRO-2026-00226

Authorization bypass — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

gRPC ListRequest for group=widget.ext.grafana.app resource=widgets verb=list from subject with wildcard scope=* and no folder permission

Attack chain
  1. /authz.v1.AuthzService/List
  2. rbac.Service.List
  3. listPermission
Runnable proof: reproduction_steps.sh
Captured evidence: fixed testfixed grpc attempt1fixed grpc attempt2
How the agent worked 615 events · 277 tool calls · 22 min
22 minDuration
277Tool calls
124Reasoning steps
615Events
4Dead-ends
Agent activity over 22 min
Support
14
Repro
546
Judge
50
0:0022:01

Root Cause and Exploit Chain for REPRO-2026-00226

Versions: Builds containing the pre-fix behavior before b9b897b3c512ee434341bb9d698eac24f90eca89 (IAM: folder-scoped authz with LIST (#126931)). The reproduction anchors the vulnerable checkout to the fixed commit's parent: 27750f0e0e3443c39f992bfe22efe3d352ee4357.

Grafana IAM LIST authorization for folder-scoped Kubernetes-native/custom resources could be bypassed when the requesting identity had a resource-type wildcard permission (scope: "*") for the CRD action but lacked folder-level authorization. In the vulnerable parent of fix commit b9b897b3c512ee434341bb9d698eac24f90eca89, pkg/services/authz/rbac.Service.listPermission returned ListResponse{All: true} as soon as scopeMap["*"] was present, before detecting mapper-miss resources such as widget.ext.grafana.app/widgets and before applying the folder-scoped authorization model. The current reproduction starts a real TCP gRPC authz.v1.AuthzService endpoint, sends a LIST request over /authz.v1.AuthzService/List, and shows that the vulnerable commit returns All=true while the fixed commit returns All=false for the same request and permission state.

  • Package/component affected: github.com/grafana/grafana, specifically pkg/services/authz/rbac and the Grafana IAM/AuthZ gRPC LIST authorization path.
  • Affected versions: Builds containing the pre-fix behavior before b9b897b3c512ee434341bb9d698eac24f90eca89 (IAM: folder-scoped authz with LIST (#126931)). The reproduction anchors the vulnerable checkout to the fixed commit's parent: 27750f0e0e3443c39f992bfe22efe3d352ee4357.
  • Risk level and consequences: High. A user or service identity with only a wildcard resource permission for a folder-scoped CRD action (for example widget.ext.grafana.app/widgets:get with scope: "*") could receive All: true from the AuthZ LIST API. That response authorizes listing across all folders even when the identity has no folder-level access, bypassing folder-scoped authorization boundaries.

Impact Parity

  • Disclosed/claimed maximum impact: Authorization bypass for folder-scoped CRD LIST authorization through the Grafana IAM/AuthZ API surface.
  • Reproduced impact from this run: Authorization bypass was reproduced over the real gRPC AuthzService LIST API boundary. The vulnerable commit returned ListResponse All=true for a folder-scoped mapper-miss CRD when the subject had only wildcard resource permission and no folder authorization. The fixed commit returned All=false for the same request.
  • Parity: full for the claimed authorization-bypass behavior and API surface.
  • Not demonstrated: The proof does not enumerate actual end-user CRD objects in a full Grafana deployment. It demonstrates the authorization decision primitive (All=true) that the LIST caller would use to list all objects.

Root Cause

The root cause is an ordering error in pkg/services/authz/rbac/service.go inside Service.listPermission. Before the fix, listPermission checked scopeMap["*"] before distinguishing mapper-hit and mapper-miss resources. Grafana's scope-map construction collapses wildcard grants into scopeMap["*"]; for ordinary resources that can be an all-resource allow. However, folder-scoped Kubernetes-native CRDs that miss the static mapper (for example groups ending in .ext.grafana.app) require a stricter model: the caller must satisfy both stack/resource permission and folder-level authorization.

Because the vulnerable implementation returned ListResponse{All: true} immediately when scopeMap["*"] was present, folder-scoped CRDs skipped the listPermissionWithFolderAuthz logic entirely. The fix in b9b897b3c512ee434341bb9d698eac24f90eca89 moves the mapper-miss/folder-scoped branch before the wildcard early return:

  • Vulnerable parent 27750f0e0e3443c39f992bfe22efe3d352ee4357: scopeMap["*"] can return All=true before folder authorization.
  • Fixed commit b9b897b3c512ee434341bb9d698eac24f90eca89: mapper-miss resources first call listPermissionWithFolderAuthz, so wildcard resource permission alone does not yield All=true.

Fix commit: https://github.com/grafana/grafana/commit/b9b897b3c512ee434341bb9d698eac24f90eca89

Reproduction Steps

  1. Use bundle/repro/reproduction_steps.sh.
  2. The script reads bundle/project_cache_context.json, reuses the prepared Grafana checkout at <project_cache_dir>/repo, resolves the fixed commit and its parent, and verifies that the vulnerable parent lacks the listPermissionWithFolderAuthz fork while the fixed commit contains it.
  3. The script writes and injects bundle/repro/repro_grpc_boundary_test.go into pkg/services/authz/rbac for each checkout. That test starts a real TCP gRPC server, registers Grafana's real rbac.Service via authzv1.RegisterAuthzServiceServer, performs a TCP health check, and sends an attacker-controlled ListRequest over /authz.v1.AuthzService/List.
  4. The request uses namespace=org-12, subject=user:test-uid, group=widget.ext.grafana.app, resource=widgets, verb=list. The configured identity has only widget.ext.grafana.app/widgets:get with scope="*" and no folder-level permission.
  5. The script runs two vulnerable attempts and two fixed attempts. Expected evidence is vulnerable All=true in both attempts and fixed All=false in both attempts.

Evidence

Primary evidence files:

  • bundle/logs/reproduction_steps.log — current-run summary and request/response excerpts for all four attempts.
  • bundle/logs/vuln_grpc_attempt1.log and bundle/logs/vuln_grpc_attempt2.log — vulnerable API-boundary request/response logs.
  • bundle/logs/fixed_grpc_attempt1.log and bundle/logs/fixed_grpc_attempt2.log — fixed negative-control API-boundary request/response logs.
  • bundle/repro/runtime_manifest.json — runtime evidence manifest showing entrypoint_kind="api_remote", service_started=true, healthcheck_passed=true, and target_path_reached=true.
  • bundle/repro/repro_grpc_boundary_test.go — the gRPC boundary test code used by the script.

Key excerpts from bundle/logs/reproduction_steps.log:

Patch check: vulnerable parent lacks listPermissionWithFolderAuthz fork; fixed commit contains it

VULNERABLE attempt 1 request/response evidence:
SERVER: Grafana AuthzService gRPC endpoint listening on 127.0.0.1:39885
HEALTHCHECK: TCP connection to Grafana AuthzService endpoint 127.0.0.1:39885 succeeded
CLIENT: sending LIST over gRPC /authz.v1.AuthzService/List namespace=org-12 subject=user:test-uid group=widget.ext.grafana.app resource=widgets verb=list permission=widget.ext.grafana.app/widgets:get scope=* no_folder_permission=true
SERVER: accepted gRPC method=/authz.v1.AuthzService/List namespace=org-12 subject=user:test-uid group=widget.ext.grafana.app resource=widgets verb=list token_prefix=pruva
SERVER: completed gRPC method=/authz.v1.AuthzService/List response All=true Folders=[] Items=[] err=<nil>
CLIENT: received ListResponse: All=true Folders=[] Items=[]

FIXED attempt 1 request/response evidence:
SERVER: Grafana AuthzService gRPC endpoint listening on 127.0.0.1:43707
HEALTHCHECK: TCP connection to Grafana AuthzService endpoint 127.0.0.1:43707 succeeded
CLIENT: sending LIST over gRPC /authz.v1.AuthzService/List namespace=org-12 subject=user:test-uid group=widget.ext.grafana.app resource=widgets verb=list permission=widget.ext.grafana.app/widgets:get scope=* no_folder_permission=true
SERVER: accepted gRPC method=/authz.v1.AuthzService/List namespace=org-12 subject=user:test-uid group=widget.ext.grafana.app resource=widgets verb=list token_prefix=pruva
SERVER: completed gRPC method=/authz.v1.AuthzService/List response All=false Folders=[] Items=[] err=<nil>
CLIENT: received ListResponse: All=false Folders=[] Items=[]

Summary:
Vulnerable attempt 1: All=true
Vulnerable attempt 2: All=true
Fixed attempt 1: All=false
Fixed attempt 2: All=false
CONFIRMED: vulnerable Grafana AuthzService gRPC LIST returns All=true across the remote API boundary, while the fixed commit returns All=false.

Environment details captured:

  • Go toolchain: go version go1.26.4 linux/amd64.
  • Repository path: /data/pruva/project-cache/6c6f6fd2-6e61-4267-8db1-032ee6a303f9/repo from the prepared project cache.
  • Vulnerable commit: 27750f0e0e3443c39f992bfe22efe3d352ee4357.
  • Fixed commit: b9b897b3c512ee434341bb9d698eac24f90eca89.

Recommendations / Next Steps

  • Keep the fix's ordering: route mapper-miss/folder-scoped resources to listPermissionWithFolderAuthz before honoring scopeMap["*"].
  • Add regression coverage that exercises the public gRPC AuthzService/List boundary for folder-scoped CRDs with wildcard resource permission but no folder permission.
  • Audit other authorization paths for wildcard early returns before resource-specific or folder-specific constraints are applied.
  • Upgrade Grafana deployments to a version containing b9b897b3c512ee434341bb9d698eac24f90eca89 or an equivalent backport.

Additional Notes

  • The reproduction script was run twice consecutively in this workspace and succeeded both times.
  • Each script run performs two vulnerable attempts and two fixed attempts, so the final current-run evidence contains four gRPC request/response executions.
  • The proof uses the real Grafana AuthZ gRPC service handler and protobuf client/server boundary. Test scaffolding supplies deterministic identity/permission/folder data so the vulnerable authorization branch is reached repeatably without requiring an external Grafana deployment.

REPRO-2026-00226 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:03
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · url-2026-06-23-grafana-issue292 · REPRO-20
0:03
0:05
web search
0:07
web search
0:11
0:12
0:14
web search
0:26
0:26
extract_facts
no facts extracted
0:28
0:28
0:28
supportrepro
0:30
0:30
0:30
0:31
0:31
0:31
0:31
0:33
0:33
0:33
1:00
error

Unknown error

1:00
1:00
1:00
1:01
1:01
1:01
1:03
08 · How to Fix

How to Fix REPRO-2026-00226

Upgrade grafana/grafana · Go to b9b897b3c512ee434341bb9d698eac24f90eca89 or later.

Coming soon

Step-by-step mitigation and hardening guidance for REPRO-2026-00226 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: REPRO-2026-00226

How does the Grafana IAM LIST authorization bypass work?

A user or service identity with a wildcard resource permission like widget.ext.grafana.app/widgets:get scoped to "*" sends a LIST request over the gRPC authz.v1.AuthzService /authz.v1.AuthzService/List endpoint; the vulnerable build returns All=true and lets the caller list all objects across every folder, while the fixed build returns All=false for the same request and permission state.

Which Grafana builds are affected, and where is it fixed?

Builds containing the code prior to fix commit b9b897b3c512ee434341bb9d698eac24f90eca89 ("IAM: folder-scoped authz with LIST (#126931)") are affected; the reproduction anchors the vulnerable checkout to that commit's parent, 27750f0e0e3443c39f992bfe22efe3d352ee4357.

How severe is this Grafana IAM LIST bypass?

It is rated high severity - an identity with only a wildcard resource-type permission can enumerate folder-scoped custom resource objects across all folders without the intended folder-level authorization check.

How can I reproduce REPRO-2026-00226?

Download the verified script from this page and run it in an isolated environment; it starts a real gRPC authz.v1.AuthzService endpoint, sends a LIST request with a wildcard-scoped permission, and shows the vulnerable commit returning All=true versus the fixed commit returning All=false for the identical request.
11 · References

References for REPRO-2026-00226

Authoritative sources for REPRO-2026-00226 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.