## Summary

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.

## Impact

- **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`:

```text
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.