name: rbac-review description: Reviews code changes for proper RBAC enforcement. Catches missing SSAR permission gates, fail-open patterns, assumed access from isAdmin, and pages that break for limited-access users. Use when reviewing PRs, auditing permissions, or checking RBAC compliance.
RBAC Evaluation Review — ODH Dashboard
Evaluates code changes for proper RBAC enforcement. The dashboard serves users with varying Kubernetes RBAC permissions determined by their cluster roles — not by any dashboard-internal user classification.
Core principle: Dashboard admins and regular users must be treated identically — every operation requires an explicit SSAR check for the specific verb+resource being accessed. The deprecated isAdmin boolean must not be used to assume capabilities. The only user who bypasses checks is a cluster admin (all SSAR pass), and since developers typically test as cluster-admin, they get a false sense that everything works. The skill catches code that will break for any limited-access user.
Inputs
The user may provide:
- A Jira ticket key (e.g.
RHOAIENG-12345) — the feature being implemented. Provides context on which resources and operations the code must gate. - No code arguments — review files changed versus
mainby runninggit diff main --name-only -- '*.ts' '*.tsx' '*.go' '*.proto', excluding**/upstream/**and**/__tests__/**paths. This ensures frontend, backend, and BFF (Go) surfaces are all scanned for RBAC issues. - A file or directory path — review only those files.
- A PR number (
#N) — rungh pr diff Nto get changed files and review those. - A branch name — validate the ref, then diff against
main.
Phase 0: Gather feature context
If the user provided a Jira ticket key as an argument (e.g. /rbac-review RHOAIENG-12345), fetch the full Jira issue (via MCP or CLI). Extract from the response:
- Summary — what feature is being built
- Description — acceptance criteria, referenced resources, API groups, verbs
- Labels —
dashboard-area-*labels hint at which subsystem is involved
Use this to build an expected permission model: which K8s resources and verbs the feature likely needs, and which user types should or should not have access. This model guides all subsequent checks — instead of only detecting generic anti-patterns, the review can flag specific missing gates for the resources/operations the feature introduces.
If no ticket was provided, proceed with generic checks only — the review still catches anti-patterns but cannot validate completeness against feature requirements.
Phase 1: Load reference data
- Read reference.md — RBAC patterns, hooks, utilities, anti-patterns.
- Skim
.claude/rules/security.mdfor general security context. - If Phase 0 produced an expected permission model, keep it active as the primary lens for all checks.
Phase 2: Classify changed code
For each changed file, determine the layer:
| Layer | Paths | Key concerns |
|---|---|---|
| Frontend pages/components | frontend/src/pages/**, packages/*/src/** |
UI gating, disabled states, route protection |
| Frontend data hooks | frontend/src/api/**, **/use*.ts |
Conditional fetching, SSAR before fetch |
| Backend routes | backend/src/routes/**, packages/*/bff/** |
secureRoute/secureAdminRoute, namespace validation |
| Models/types | **/models.ts, **/k8sTypes.ts |
New resources that need SSAR definitions |
Phase 3: Run checks
Check 1: Missing permission gates on new functionality
For any new page, route, button, kebab action, or data-fetching hook:
- Frontend pages/routes — Verify
accessAllowedRouteHoC,useAccessAllowed, oruseAccessReview+ conditional rendering is applied. Routes withflags: { required: [ADMIN_USER] }in the extension system are protected at the plugin layer — flag as Info (should migrate toaccessAllowedRouteHoC) rather than Critical. A new page with no SSAR gate at all is a Critical finding. - Buttons/actions — Verify the action checks permissions (via
useAccessAllowed,useAccessReview,useKebabAccessAllowed, orAccessAllowedrender-prop). A mutation action without a permission check is Warning. - Backend routes — Verify mutating endpoints are protected.
secureRouteonly enforces namespace/resource validation on parameterized requests (routes with:namespaceor resource ID params); un-parameterized POST/PUT/PATCH/DELETE routes are only logged, not blocked. For un-parameterized mutating endpoints, requiresecureAdminRouteor an explicit backend SSAR check (createSelfSubjectAccessReview). An unprotected or insufficiently protected mutating endpoint is Critical.
Check 2: Fail-open patterns
Flag code that defaults to "allowed" when permission checks fail or are missing:
checkAccesscatches returningtrue— this is an intentional fail-open for the UI. Verify that the backend still enforces the gate. If only frontend SSAR is used with no backend protection for a mutation, flag as Critical.- Conditionals like
if (isAdmin || true), disabled checks behind feature flags that default to off, orisAllowedstates initialized totruebefore the SSAR resolves — flag as Critical. - Loading states that render privileged UI before SSAR completes — flag as Warning.
Check 3: Assumed access / deprecated admin patterns
The isAdmin boolean means "can patch auths/default-auth" — it does NOT mean the user can do arbitrary operations. Code that treats isAdmin as a universal access pass skips the actual SSAR check and will break for users who are "dashboard admins" but lack specific permissions.
Flag usage of:
useUser().isAdminor Reduxstate.user.isAdminto gate new features — Critical (assumes capabilities instead of checking them; useuseAccessAllowedwithverbModelAccessfor the specific operation).- Any pattern like
if (isAdmin) { fetchResource() }without also checking whether the user can actually access that resource — Critical. getClusterAdminUserListor Group-based admin checks — Warning (deprecated).- Direct import of
isUserAdminon the frontend — Warning (backend-only utility).
Exception: Routes gated via flags: { required: [ADMIN_USER] } in the extension system are NOT the same as using isAdmin directly — they are protected at the plugin layer (see Check 1). Flag as Info (deprecated-but-functional; recommend migration to accessAllowedRouteHoC with verbModelAccess).
Check 4: Namespace and resource scoping
- Backend: Verify namespace validation in route handlers. Operations must be scoped to
dashboardNamespaceorworkbenchNamespace. Note:secureRouteonly enforces namespace validation on parameterized requests (see Check 1); un-parameterized mutating routes may still allow cross-namespace mutations. Treat un-scoped mutating endpoints as Critical unless the handler explicitly enforcesdashboardNamespace/workbenchNamespace. - Frontend: SSAR checks without a namespace default to the dashboard namespace (via
AccessReviewProvider). Verify this is intentional when working with resources in other namespaces — Warning if ambiguous. - BFF (Go): Verify token forwarding and namespace validation in proxy calls. Missing auth header propagation is Critical.
Check 5: Graceful degradation for limited-access users
The most common bug: a developer tests with cluster-admin, everything works. A limited user navigates to the same page, a GET request fails with 403, and the page breaks or shows an error instead of degrading gracefully.
For any page, data fetch, or UI state that depends on a resource:
- Pages — If a user lacks permission to
list/getthe primary resource, the page must either be hidden from navigation (viaaccessReviewon the route/extension) or render a meaningful empty/denied state. Showing a broken page or unhandled error is Critical. - Conditional UI — Features that only appear for users with specific permissions (e.g., a "Create" button requiring
createverb) must use SSAR to determine visibility/disabled state. Assuming "dashboard admin can always create" is Critical. - Fallback states — When a secondary data fetch fails due to permissions (e.g., loading cluster-wide metrics on a page that also works without them), the page must continue functioning with the available data. Treating the 403 as a fatal error is Warning.
Flag code that uses the deprecated isAdmin to gate features instead of checking the specific verb+resource — this creates a false binary where "admin" is assumed to have access without verification.
Check 6: Data exposure in hooks
For data-fetching hooks that load sensitive or admin-only data:
- Verify the hook conditionally fetches based on permission (e.g.,
shouldRunCheckparameter, or wrapping inuseAccessAllowed). - Hooks that unconditionally fetch admin-only resources and rely solely on the backend to reject are acceptable if the backend enforces it, but flag as Info if the frontend could avoid the failed request.
Phase 4: Generate report
## RBAC Review — ODH Dashboard
### Feature Context (if Jira ticket provided)
- Ticket: RHOAIENG-XXXXX — <summary>
- Resources: <K8s resources the feature touches>
- Expected gates: <verb/resource pairs that need SSAR checks>
- Limited-user behavior: <what should happen when SSAR denies access>
### Summary
- Files reviewed: N
- Findings: N
- By severity: N critical, N warning, N info
- Permission model coverage: N/M expected gates implemented
### Critical (must fix before merge)
### Warning (should fix)
### Info (consider / acceptable with justification)
---
**[SEVERITY] Check N: Description**
- File: `path/to/file.ts`
- Line: NN
- Found: `the problematic code or pattern`
- Expected: `what the code should do`
- Why: Brief explanation of the RBAC risk
- User impact: Which user type is affected and how
Omit the "Feature Context" and "Permission model coverage" sections if no Jira ticket was provided. Instead, append a note at the end of the report:
---
> **Note:** This review checked for generic RBAC anti-patterns only. For a more targeted review that validates permission coverage against feature requirements, re-run with a Jira ticket: `/rbac-review RHOAIENG-XXXXX`
Severity classification
| Severity | Criteria |
|---|---|
| Critical | Missing backend auth on mutating endpoints; unprotected pages/routes; fail-open patterns that bypass Kubernetes RBAC; using isAdmin to assume capabilities; pages that break for limited users |
| Warning | Missing frontend permission gates on actions; ambiguous namespace scoping; loading-state privilege leaks; secondary fetches that 403 without fallback |
| Info | Unconditional data fetch that backend rejects (works but wasteful); patterns that function correctly but could be cleaner |
If there are no findings, confirm the changes have proper RBAC coverage and note any well-implemented patterns worth highlighting.