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.
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.
REPRO-2026-00226 Severity
REPRO-2026-00226 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
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 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 Proof of Reproduction for REPRO-2026-00226
- reached the target end-to-end
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
gRPC ListRequest for group=widget.ext.grafana.app resource=widgets verb=list from subject with wildcard scope=* and no folder permission
- /authz.v1.AuthzService/List
- rbac.Service.List
- listPermission
reproduction_steps.sh How the agent worked
Root Cause and Exploit Chain for REPRO-2026-00226
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, specificallypkg/services/authz/rbacand 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:getwithscope: "*") could receiveAll: truefrom 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=truefor a folder-scoped mapper-miss CRD when the subject had only wildcard resource permission and no folder authorization. The fixed commit returnedAll=falsefor the same request. - Parity:
fullfor 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 returnAll=truebefore folder authorization. - Fixed commit
b9b897b3c512ee434341bb9d698eac24f90eca89: mapper-miss resources first calllistPermissionWithFolderAuthz, so wildcard resource permission alone does not yieldAll=true.
Fix commit: https://github.com/grafana/grafana/commit/b9b897b3c512ee434341bb9d698eac24f90eca89
Reproduction Steps
- Use
bundle/repro/reproduction_steps.sh. - 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 thelistPermissionWithFolderAuthzfork while the fixed commit contains it. - The script writes and injects
bundle/repro/repro_grpc_boundary_test.gointopkg/services/authz/rbacfor each checkout. That test starts a real TCP gRPC server, registers Grafana's realrbac.Serviceviaauthzv1.RegisterAuthzServiceServer, performs a TCP health check, and sends an attacker-controlledListRequestover/authz.v1.AuthzService/List. - The request uses
namespace=org-12,subject=user:test-uid,group=widget.ext.grafana.app,resource=widgets,verb=list. The configured identity has onlywidget.ext.grafana.app/widgets:getwithscope="*"and no folder-level permission. - The script runs two vulnerable attempts and two fixed attempts. Expected evidence is vulnerable
All=truein both attempts and fixedAll=falsein 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.logandbundle/logs/vuln_grpc_attempt2.log— vulnerable API-boundary request/response logs.bundle/logs/fixed_grpc_attempt1.logandbundle/logs/fixed_grpc_attempt2.log— fixed negative-control API-boundary request/response logs.bundle/repro/runtime_manifest.json— runtime evidence manifest showingentrypoint_kind="api_remote",service_started=true,healthcheck_passed=true, andtarget_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/repofrom 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
listPermissionWithFolderAuthzbefore honoringscopeMap["*"]. - Add regression coverage that exercises the public gRPC
AuthzService/Listboundary 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
b9b897b3c512ee434341bb9d698eac24f90eca89or 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.
Unknown error
Artifacts and Evidence for REPRO-2026-00226
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix REPRO-2026-00226
Upgrade grafana/grafana · Go to b9b897b3c512ee434341bb9d698eac24f90eca89 or later.
FAQ: REPRO-2026-00226
How does the Grafana IAM LIST authorization bypass work?
Which Grafana builds are affected, and where is it fixed?
How severe is this Grafana IAM LIST bypass?
How can I reproduce REPRO-2026-00226?
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.