Skip to content

fix(api): harden API key management auth#2403

Open
riderx wants to merge 70 commits into
mainfrom
codex/rbac-apikey-management-hardening
Open

fix(api): harden API key management auth#2403
riderx wants to merge 70 commits into
mainfrom
codex/rbac-apikey-management-hardening

Conversation

@riderx

@riderx riderx commented Jun 3, 2026

Copy link
Copy Markdown
Member

Summary (AI generated)

  • Removed stale route-level key-mode authorization such as middlewareV2(['all', 'write']); handlers now authenticate first and enforce authorization through RBAC checks, API route guards, and RLS.
  • Removed app-side use_new_rbac runtime gating while keeping the compatibility output field and old SQL compatibility helpers.
  • Hardened /apikey management: broad non-limited all API keys retain sibling key update/delete compatibility, while limited scoped keys are filtered out and all API keys are blocked from key creation, self-mutation, and role-binding replacement.
  • Hardened org settings writes: orgs UPDATE is now guarded by the named RBAC permission org.update_settings, use_new_rbac is removed as a downgrade switch, rollback cannot disable RBAC, and cli organization set now calls PUT /organization instead of legacy direct-write semantics.
  • Added explicit backend validation for password_policy_config so moving CLI org settings to the API route does not drop the password-policy feature.
  • Fixed the events notify-console path so it relies on the verified org/app resolution instead of an org-only preflight that broke app-scoped authorization.
  • Fixed app-reader channel visibility: app-scoped app_reader now grants read-only channel visibility for every current and future channel under that app, while channel_reader stays channel-only.
  • Aligned the channel permission override UI with the RBAC matrix so app_reader defaults show read-only channel access, not stale deny labels.
  • Reduced Sonar failure-level complexity in changed RBAC UI and bundle set-channel code without changing authorization behavior.

Motivation (AI generated)

RBAC is now the authoritative authorization model. Leaving old key-mode write labels, app-side RBAC feature flags, and CLI direct org writes made the active security boundary ambiguous and could preserve upgrade/downgrade paths that no longer match the product model. This PR removes those stale surfaces while preserving intentional compatibility for old API-key read paths and old org payload shape.

Fix Justification (AI generated)

  • Auth-only middleware defaults: old ['all', 'write'] route declarations are no longer the security boundary and were misleading.
  • API-key mutation guard: a leaked API key, even a broad one, must not be able to update/regenerate/delete API keys or upgrade its own API-key RBAC bindings.
  • /apikey read/list compatibility: keeping API-key reads avoids breaking old compatible callers; scoped keys still use the existing limited-scope filter.
  • Org RLS hardening: orgs UPDATE is named-RBAC guarded for anon/authenticated callers through org.update_settings; it no longer uses old key-mode write labels or use_new_rbac as an authorization source.
  • CLI org settings route migration: cli organization set no longer relies on legacy direct Supabase orgs.update(...) semantics.
  • Password-policy route validation: the backend route now accepts only a typed policy object with min_length bounded to 6..72, matching the DB constraint.
  • App-reader channel visibility: app.read_channels is the route-level list permission, but channel table RLS checks concrete channel.read* permissions; app_reader now carries only read-only channel permissions at app scope so new channels are readable without granting channel writes.
  • Compatibility helpers: old RPC names remain callable for older CLIs, but they resolve through RBAC role bindings instead of old stored rights.

RBAC Rights Map (AI generated)

This map is generated from the current RBAC schema after the cleanup migration. The important security boundary is: callers do not get rights from old labels like write, read, upload, all, or org membership metadata. Rights come from role_bindings -> roles -> role_permissions, checked at the matching org/app/channel scope.

Request Authorization Flow (AI generated)

flowchart TD
  Caller["Caller"] --> AuthKind{"JWT user or API key?"}
  AuthKind -->|JWT| UserPrincipal["principal_type=user<br/>principal_id=auth.uid()"]
  AuthKind -->|API key| ApiKeyLookup["find_apikey_by_value()<br/>reject expired/invalid keys"]
  ApiKeyLookup --> ApiPrincipal["principal_type=apikey<br/>principal_id=apikey.rbac_id"]
  UserPrincipal --> Bindings["role_bindings<br/>scoped to org/app/channel"]
  ApiPrincipal --> Bindings
  Bindings --> Permissions["roles + inherited roles<br/>role_permissions"]
  Permissions --> Check["rbac_has_permission(permission_key, scope)"]
  Check --> RLS["RLS: rbac_check_permission_request(...)"]
  Check --> Backend["Backend/CLI: checkPermission/assertCliPermission(...)"]
  RLS --> RowAllowed["Row allowed only for matching org/app/channel"]
  Backend --> RouteAllowed["Route continues only for the named permission"]
Loading

Built-In Role Inheritance (AI generated)

flowchart LR
  OSA["org_super_admin"] --> OA["org_admin"]
  OA --> AA["app_admin"]
  AA --> AD["app_developer"]
  AD --> AU["app_uploader"]
  AU --> AR["app_reader"]
  AA --> CA["channel_admin"]
  CA --> CR["channel_reader"]
  AA --> BA["bundle_admin"]
  BA --> BR["bundle_reader"]
  OB["org_billing_admin"]
  OM["org_member"]
  AKR["apikey_org_reader"]
Loading

Current Role Grants To Watch (AI generated)

Role Important direct/effective grants
org_super_admin org_admin plus destructive/admin grants such as org.delete, app.delete, app.transfer, bundle.delete, channel.delete, org.update_user_roles, app.update_user_roles.
org_admin Org/app admin without destructive org/app delete: org reads/billing/audit, org.update_settings, org.update_user_roles, org.invite_user, org.create_app, app settings, app upload/build/device/log reads, channel promote/rollback/forced-device/settings.
org_billing_admin Billing path: org.read, org.read_billing, org.read_billing_audit, org.read_invoices, org.update_billing, plus org.create_app.
org_member Minimal org metadata: org.read, org.read_members, org.create_app.
app_admin App-scoped admin: app settings/user-role updates, app create channel, upload/build/devices/logs, bundle delete, channel delete/settings/promote/rollback/forced devices.
app_developer Upload/build/device/log/channel operational rights. It does not grant channel.update_settings after this PR.
app_uploader App read/log/device/bundle reads plus app.upload_bundle.
app_reader App read-only set plus read-only channel metadata/history/forced-device/audit access for every current and future channel in the app. It does not grant channel settings, promote, rollback, forced-device management, or delete.
channel_admin Channel-scoped settings/delete/promote/rollback/forced-device management plus channel read/history.
channel_reader Channel read/history/forced-device read only.
apikey_org_reader API-key compatibility role with only org.read.

App And Channel Visibility Rules (AI generated)

Binding App row visible? Channel rows visible? Notes
App-scoped app_reader Yes, through app.read. Yes, every current and future channel in that app through read-only channel.read* grants. This is the intended "app read + all channel read" assignment.
Channel-scoped channel_reader No. Only the bound channel. This avoids silently expanding a channel-only grant into app-level metadata access. UI flows that need the app shell must also assign app-level read.
App-scoped app_developer Yes. Yes, plus deploy/rollback/forced-device operational rights. Still no channel.update_settings after this PR.

Sensitive Operation Gates (AI generated)

Surface Permission gate Scope passed to RBAC Security note
API-key table mutation No client permission n/a apikeys has explicit deny policies for client insert/update; API-key anon select/delete are denied; JWT users can select/delete only their own keys.
API-key role binding mutation org.update_user_roles or app.update_user_roles Org/app/channel from the target binding role_bindings write policies are TO authenticated; API-key traffic cannot grant itself bindings through RLS. Priority and last-super-admin triggers still apply.
Org settings org.update_settings org_id orgs UPDATE is RBAC-only and no longer tied to old write labels or use_new_rbac.
Org membership, groups, invites org.update_user_roles; invites also require org.invite_user; super-admin invite path requires update-user-roles org_id Membership metadata in org_users does not grant rights by itself; role assignment writes must pass RBAC.
App settings app.update_settings owner_org, app_id App read is not enough to mutate app config.
App creation org.create_app owner_org App insert is scoped to the owning org.
App/channel role assignment app.update_user_roles owner_org, app_id, optional channel_id Used for app and channel-scoped role bindings.
Channel settings channel.update_settings owner_org, app_id, channel_id Channel-scoped checks must pass the concrete channel id; app developer no longer gets this permission.
Channel forced devices channel.manage_forced_devices / channel.read_forced_devices owner_org, app_id, channel_id Insert/update/delete forced-device overrides require manage permission; reads use read-forced-devices.
Bundle upload app.upload_bundle owner_org, app_id Old upload CLIs reach this through compatibility RPCs, but the authorization check is still RBAC.
Bundle update/delete bundle.update / bundle.delete owner_org, app_id Delete is separate from upload/update.
Build logs/native build app.read_logs / app.build_native owner_org, app_id Operational reads/builds are not gated by broad admin checks.
Storage app bundle paths app.read_bundles, app.upload_bundle, bundle.delete Folder app id resolved to owner org/app Storage policies delegate to RBAC helpers, not key modes.

Legacy CLI Compatibility Vocabulary (AI generated)

Old RPC names can still be called by older CLIs, but old rights are not stored or used as policy sources. The compatibility RPCs translate the requested legacy word into current RBAC checks:

Legacy RPC word Current RBAC permission checked
read Any of org.read, app.read, app.read_bundles, channel.read, depending on whether the old RPC has app scope.
upload app.upload_bundle.
write Any of app.update_settings, app.create_channel, bundle.update, channel.update_settings.
all Any of org.delete, org.update_user_roles, app.delete, app.update_user_roles, app.transfer.

Current schema cleanup checks show no key_mode type/column and no user_min_right type/column. These words remain only as old RPC input vocabulary for previous CLI versions.

Business Impact (AI generated)

This reduces the chance of API-key self-escalation, leaked-key damage, or accidental reactivation of legacy write behavior, while avoiding a breaking change for compatible API-key read/list callers and current CLI org-settings workflows.

Test Plan (AI generated)

  • bun lint:backend
  • bun typecheck:backend
  • commit hook: bun run cli:typecheck && bun run typecheck:backend && bun run typecheck:frontend
  • bun lint
  • bun run cli:check
  • bun run supabase:with-env -- bunx vitest run tests/rbac-permissions.test.ts --reporter verbose
  • bunx eslint tests/rbac-permissions.test.ts
  • bun run supabase:db:reset
  • PGSSLMODE=disable bunx supabase test db --db-url postgresql://postgres:postgres@127.0.0.1:60642/postgres supabase/tests/00-supabase_test_helpers.sql supabase/tests/26_test_rls_policies.sql supabase/tests/48_test_rbac_admin_rpc_execute_grants.sql
  • PGSSLMODE=disable bunx supabase test db --db-url postgresql://postgres:postgres@127.0.0.1:60642/postgres supabase/tests/26_test_rls_policies.sql
  • bun test --timeout 60000 tests/events.test.ts tests/apikeys.test.ts tests/apikeys-expiration.test.ts tests/organization-api.test.ts tests/audit-logs.test.ts tests/password-policy.test.ts
  • bunx vitest run tests/organization-put-stripe-sync.unit.test.ts
  • bun run supabase:with-env -- bunx vitest run tests/hashed-apikey-rls.test.ts tests/private-channel-device-rbac.test.ts tests/cli-app-permission-helper.test.ts --reporter verbose
  • bun run test:backend
  • bunx vitest run tests/frontend-channel-rbac-scope.test.ts --reporter verbose
  • bunx eslint src/components/permissions/ChannelPermissionOverridesPanel.vue tests/frontend-channel-rbac-scope.test.ts
  • bun run supabase:with-env -- bunx vitest run tests/bundle-set-channel-rbac.unit.test.ts --reporter verbose
  • GitHub CI after latest push: all checks green; Supabase Preview skipped.
  • GitHub CI after rerun: all checks green.

Generated with AI

Summary by CodeRabbit

  • New Features

    • API key management now enforces centralized authorization with improved permission controls.
    • Organization settings can be updated via the API, enabling better programmatic management.
  • Improvements

    • Authorization system simplified to use role-based permissions exclusively, removing legacy compatibility modes.
    • Organization membership and invitations now use a cleaner role model for improved consistency.
    • API key creation and validation streamlined with stronger permission checks.

Note

High Risk
Touches authorization paths across CLI upload/channel/org flows and documents RBAC-only DB policy expectations; misaligned permission keys or API routing could deny legitimate operations or leave inconsistent client vs server behavior.

Overview
CLI authorization no longer gates commands with OrganizationPerm / org key modes. App, bundle, channel, and upload flows now require explicit RBAC keys (e.g. app.upload_bundle, channel.promote_bundle, channel.delete) via hasCliPermission, including per-channel checks where needed. Upload/channel set paths add preflight and bundle-function promotion so missing promote/create rights fail clearly instead of relying on broad “write/all” semantics.

organization set stops direct Supabase orgs updates for 2FA, password policy, and API-key org settings; it resolves the functions API host and calls PUT /organization, and uses request_actor_user_id instead of legacy identity RPCs for 2FA member checks.

app set gains optional flags (device custom ID, provider infra blocking, build timeout, store URLs, default upload/download channels, disable download channels) with channel validation helpers. Preview QR adds --png, --url, --web-url, and --preview-env plus shared preview subdomain encoding.

Docs/config: AGENTS.md and a slim RBAC_SYSTEM.md document RBAC-only RLS (rbac_check_permission_request, no get_identity* / min-right helpers). Sonar excludes read_replicate/schema_replicate.sql; deploy workflow has a trivial formatting change. CLI bumps to 8.19.0 with new tests wired in package.json.

Reviewed by Cursor Bugbot for commit 24b9fa5. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces legacy key-mode/min-rights with RBAC-only checks. Updates middleware, handlers, SQL policies/migrations, generated types, seeds, CLI permission flow, frontend role handling, and tests/docs. Centralizes API-key management with JWT-only operations and ownership scoping. Removes obsolete functions/enums and aligns RLS assertions.

Changes

End-to-end RBAC unification across services

Layer / File(s) Summary
RBAC refactor, policy/type updates, and test/CLI/frontend alignment
supabase/functions/_backend/..., supabase/schemas/*.sql, supabase/migrations/*, supabase/seed.sql, cli/src/*, src/*, tests/*, supabase/tests/*, docs/*
Middleware switched to middlewareAuth/middlewareKey(options). Handlers enforce RBAC via checkPermission; API key management centralized. Schema/policies/types/seeds updated to RBAC (remove key_mode/user_min_right). CLI uses string permission keys; frontend roles/ranks updated. Tests/docs realigned to RBAC and removed legacy helpers.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Cap-go/capgo#2391 — Also updates API key management in public/apikey/put.ts with RBAC-focused changes.
  • Cap-go/capgo#1997 — Touches private/create_device.ts to validate org scope against app owner; overlaps with this PR’s RBAC checks.
  • Cap-go/capgo#2330 — Modifies /private/events handler; intersects with this PR’s middleware/auth and parsing refactor.

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing codex/rbac-apikey-management-hardening (b6bf7d5) with main (69545ad)2

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (9be212f) during the generation of this report, so 69545ad was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from 2d98712 to 0ceea8a Compare June 3, 2026 12:52
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from 0ceea8a to 7489bf9 Compare June 3, 2026 13:05
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from 7489bf9 to 15a2742 Compare June 3, 2026 13:18
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from 15a2742 to 0f07372 Compare June 3, 2026 13:26
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from 0f07372 to 6d7e2b2 Compare June 3, 2026 13:30
@riderx riderx marked this pull request as ready for review June 3, 2026 13:48
@coderabbitai coderabbitai Bot added the codex label Jun 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
supabase/tests/26_test_rls_policies.sql (1)

377-393: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Assert the new deny policies are actually restrictive.

This only proves the three policy names exist. If a future migration recreates any of them as PERMISSIVE, the test still passes while the deny becomes ineffective against the existing owner allow-policies. Please add pg_policies assertions for permissive = 'RESTRICTIVE' and the expected roles/cmd on these three new policies.

As per coding guidelines: Add explicit deny policies for operations that must be impossible for user-facing roles, using RESTRICTIVE policies instead of relying on implicit deny.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/tests/26_test_rls_policies.sql` around lines 377 - 393, The test
currently only checks that policy names exist for table apikeys via policies_are
but does not verify they are restrictive; add assertions that query pg_policies
for the three deny policies (e.g., 'Deny anon delete on apikeys', 'Deny anon
select on apikeys', 'Deny anon update on apikeys') and assert permissive =
'RESTRICTIVE' and that the role(s) and cmd columns match the expected values for
each policy; locate this near the existing policies_are call for apikeys and add
one assertion per deny policy checking pg_policies.permissive, pg_policies.roles
and pg_policies.cmd to ensure the denies are actually restrictive.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@supabase/functions/_backend/public/apikey/auth.ts`:
- Around line 18-21: The code reads auth.authType without guarding for missing
auth; change the declaration to allow undefined (e.g., const auth =
c.get('auth') as AuthInfo | undefined) and update the guard to check for missing
auth first (if (!auth || auth.authType !== 'jwt' || !auth.userId) { ... }), and
when building the quickError payload use optional chaining for the authType
field (auth?.authType) so a missing auth produces the intended 401 instead of a
500; keep the same quickError call, errorCode, action and moreInfo variables.

In `@tests/apikeys-expiration.test.ts`:
- Around line 807-812: The test block removed an endpoint-level check, leaving
only direct Supabase/RLS reads (expectApiKeyCannotReadBaseOrg and
expectApiKeyCanReadBaseOrg); restore one HTTP-path assertion against the /apikey
endpoint in this block so middleware/header-parsing is exercised—add a single
assertion that the expiredKeyValue is rejected when calling the /apikey HTTP
route (alongside the RLS helper) and keep the validKeyValue HTTP-path check in
the other test block, referencing the existing helpers for RLS reads and the
/apikey route to locate where to add it.

In `@tests/apikeys.test.ts`:
- Around line 1030-1077: The test "plain key cannot update apikeys table
directly through RLS" leaves the created API key if an assertion fails; wrap the
cleanup DELETE call in a finally block so the seeded key is always removed.
Specifically, after creating the key (createResponse / createData), move the
fetch DELETE for `/apikey/${createData.id}` into a finally section that runs
regardless of test success, keeping the existing authHeaders and preserving the
rest of the assertions in the try block; ensure createData.id is available in
the finally (declare it in the outer scope if needed) so cleanup always
executes.

---

Outside diff comments:
In `@supabase/tests/26_test_rls_policies.sql`:
- Around line 377-393: The test currently only checks that policy names exist
for table apikeys via policies_are but does not verify they are restrictive; add
assertions that query pg_policies for the three deny policies (e.g., 'Deny anon
delete on apikeys', 'Deny anon select on apikeys', 'Deny anon update on
apikeys') and assert permissive = 'RESTRICTIVE' and that the role(s) and cmd
columns match the expected values for each policy; locate this near the existing
policies_are call for apikeys and add one assertion per deny policy checking
pg_policies.permissive, pg_policies.roles and pg_policies.cmd to ensure the
denies are actually restrictive.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9d385eca-b762-452d-a898-93a462ad60c4

📥 Commits

Reviewing files that changed from the base of the PR and between e64b645 and 6d7e2b2.

📒 Files selected for processing (10)
  • cli/src/types/supabase.types.ts
  • supabase/functions/_backend/public/apikey/auth.ts
  • supabase/functions/_backend/public/apikey/delete.ts
  • supabase/functions/_backend/public/apikey/get.ts
  • supabase/functions/_backend/public/apikey/put.ts
  • supabase/functions/_backend/public/apikey/scope.ts
  • supabase/migrations/20260603120805_deny_apikey_management_from_api_key_auth.sql
  • supabase/tests/26_test_rls_policies.sql
  • tests/apikeys-expiration.test.ts
  • tests/apikeys.test.ts
💤 Files with no reviewable changes (1)
  • supabase/functions/_backend/public/apikey/scope.ts

Comment thread supabase/functions/_backend/public/apikey/auth.ts Outdated
Comment thread tests/apikeys-expiration.test.ts Outdated
Comment thread tests/apikeys.test.ts Outdated
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from 6d7e2b2 to ed6303b Compare June 3, 2026 14:06
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from ed6303b to a8fc38e Compare June 3, 2026 14:14
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from a8fc38e to 3991392 Compare June 3, 2026 14:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cli/src/types/supabase.types.ts`:
- Around line 3037-3045: The RPC type for app_versions_has_app_permission
incorrectly requires both p_apikey and p_user_id as non-null strings; update the
SQL RPC signature so the inactive auth parameter is nullable (make p_apikey OR
p_user_id NULLABLE/optional in the function definition for
app_versions_has_app_permission), deploy the migration, then regenerate the
TypeScript types (run the project type generation command, e.g. `bun types`) so
the generated supabase.types.ts reflects p_apikey and p_user_id as
nullable/optional and callers no longer need unsafe casts or dummy values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 81ad7474-945a-4192-9133-1c57dfd028ca

📥 Commits

Reviewing files that changed from the base of the PR and between 6d7e2b2 and a8fc38e.

📒 Files selected for processing (10)
  • cli/src/types/supabase.types.ts
  • supabase/functions/_backend/public/apikey/auth.ts
  • supabase/functions/_backend/public/apikey/delete.ts
  • supabase/functions/_backend/public/apikey/get.ts
  • supabase/functions/_backend/public/apikey/put.ts
  • supabase/functions/_backend/public/apikey/scope.ts
  • supabase/migrations/20260603120805_deny_apikey_management_from_api_key_auth.sql
  • supabase/tests/26_test_rls_policies.sql
  • tests/apikeys-expiration.test.ts
  • tests/apikeys.test.ts
💤 Files with no reviewable changes (1)
  • supabase/functions/_backend/public/apikey/scope.ts

Comment thread cli/src/types/supabase.types.ts Outdated
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from 3991392 to d1b7c51 Compare June 3, 2026 15:34
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from d1b7c51 to fea72cb Compare June 3, 2026 15:41

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fea72cb875

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/_backend/public/organization/audit.ts
@riderx riderx force-pushed the codex/rbac-apikey-management-hardening branch from fea72cb to 507af66 Compare June 3, 2026 15:52
Comment thread cli/src/bundle/upload.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Cursor Bugbot reported 1 unresolved issue and its check finished as skipped, so automated approval signals are incomplete. This auth/RBAC hardening PR exceeds the low-risk threshold; human review is required.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot reported 1 unresolved issue and its check finished as skipped; this broad auth/RBAC security change exceeds the approval threshold and needs human review. Assigned WcaleNieWolny and Dalanir as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

Comment thread cli/src/channel/set.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot reported 1 unresolved issue and its check finished as skipped; this auth/RBAC hardening PR exceeds the approval threshold. WcaleNieWolny and Dalanir are already assigned as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot reported 1 unresolved issue and its check finished as skipped; this broad auth/RBAC security change exceeds the low-risk approval threshold. Human review is required—WcaleNieWolny and Dalanir are already assigned as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

Comment thread cli/src/channel/set.ts
Comment thread cli/src/bundle/upload.ts
Comment thread cli/src/bundle/upload.ts
Comment thread cli/src/channel/delete.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Cursor Bugbot skipped without a <!-- BUGBOT_REVIEW --> comment, so the required automated signal is missing. Human review is still needed for this auth/RBAC API key hardening change. Reviewers already requested: WcaleNieWolny, Dalanir.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot reported 4 unresolved issues on the latest commit and its check finished as skipped; this auth/RBAC security change exceeds the low-risk approval threshold. WcaleNieWolny and Dalanir are already assigned as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

Comment thread cli/src/bundle/upload.ts
Comment thread cli/src/channel/set.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot finished as skipped and reported 2 potential issues, and this auth/RBAC hardening change exceeds the low-risk approval threshold. WcaleNieWolny and Dalanir are already requested as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot finished as skipped and reported 2 potential issues on the latest commit, and this auth/RBAC security hardening exceeds the medium-risk approval threshold. WcaleNieWolny and Dalanir are already requested as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

Comment thread cli/src/bundle/upload.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot skipped and reports 2 unresolved issues on this auth/RBAC API key hardening change. Human review still needed; reviewers already requested (WcaleNieWolny, Dalanir).

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot found 1 new potential issue (2 unresolved total) on the latest commit, and this auth/RBAC security hardening exceeds the low-risk approval threshold. WcaleNieWolny and Dalanir are already requested as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 496e912. Configure here.

Comment thread cli/src/bundle/upload.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot finished as skipped and reported 1 potential issue on the latest commit; this auth/RBAC security hardening exceeds the low-risk approval threshold. WcaleNieWolny and Dalanir are already requested as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Risk: high. Not approving: Cursor Bugbot skipped and reported 1 potential issue on the latest commit, and this auth/RBAC security hardening exceeds the medium-risk approval threshold. WcaleNieWolny and Dalanir are already requested as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk: high. Not approving: this auth/RBAC API key hardening PR exceeds the low-risk approval threshold, and Cursor Bugbot passed on the latest commit without a corresponding <!-- BUGBOT_REVIEW --> comment. WcaleNieWolny and Dalanir are already requested as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk: high. Not approving: Cursor Bugbot passed on the latest commit but no <!-- BUGBOT_REVIEW --> comment was posted for it, and the prior Bugbot review reported 1 potential issue on this auth/RBAC hardening PR. WcaleNieWolny and Dalanir are already requested as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

* fix(api): allow API key management via org.manage_apikeys RBAC role

Introduce org.manage_apikeys and apikey_manager so legacy broad keys and
dedicated CI keys can create and manage sibling keys without user-role
assignment rights. Skip 2FA enforcement on API-key auth paths, optimize
manifest/app_versions RLS with readable app id helpers, and align seed
data with app_uploader channel promote and channel_admin legacy mapping.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(api): add seeded apikey_manager key and management coverage

Seed API key 113 with the apikey_manager role and add vitest/SQL checks
that CI keys can manage siblings without role escalation privileges.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(seed): repair apikey_manager bindings after RBAC repopulation

Re-apply org-scoped RBAC bindings for the dedicated apikey management
seed keys after permissions are repopulated, and assert by key UUID in
the SQL test instead of a fixed apikeys.id.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(api): address RBAC apikey_manager PR review feedback

Grant apikey_manager org.read for expiration policy enforcement, block
admin-tier role assignment from apikey_manager callers, and restore
narrow channel_developer/uploader legacy mappings with first-class roles.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(api): scope apikey_manager role denial checks per org

Only evaluate denied assignable roles for bindings in orgs where the
caller lacks org.update_user_roles.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(api): harden apikey_manager seed, bindings, and bundle RLS

Strip client-controlled allowSystemRole from binding input, upsert the
apikey_manager role during seed repopulation, restore org_admin channel
settings permissions in seed, and reject JWT/API-key owner mismatches in
app_versions_readable_app_ids().

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(seed): preserve apikey_manager test key bindings

Skip server key regeneration for seeded management API keys and repair
bindings by stable apikey id so CI seed assertions stay deterministic.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(seed,tests): stabilize apikey_manager seed and CLI preflight cases

Re-apply management key bindings after seed completes with a fail-fast
check, and use app_reader in channel-promotion CLI tests now that
app_uploader includes channel.promote_bundle.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tests): reassert apikey 113 binding in RBAC SQL test setup

Ensure test 8 is self-contained inside its transaction so parallel pgTAP
files cannot leave apikey 113 without the apikey_manager org binding.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tests): align CLI upload preflight with app_uploader RBAC

app_uploader now includes channel.promote_bundle, so update the SDK
preflight integration tests to assert the upload succeeds instead of
expecting a missing-permission failure.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tests): run apikey seed assertions before authenticated role

Seed binding checks query apikeys, which are hidden from authenticated
users by RLS. Run those assertions as postgres before authenticate_as.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tests): expect api keys to bypass org 2FA in channel RBAC checks

rbac_check_permission_request now skips 2FA enforcement for API key
principals, including channel-scoped keys without app.read bindings.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tests): assert nested apikey creation blocked for non-manager keys

org_super_admin API keys can now create sibling keys, so use org_member
test keys and expect 401 cannot_create_apikey for nested creation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(security): restrict group metadata access to members and admins (#2585)

* fix(security): restrict group metadata access to members and admins

Prevent org members from reading groups and group_members via PostgREST unless they belong to the group or hold org admin rights.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(security): run group RLS tests sequentially

Avoid parallel fixture mutation when the join regression test temporarily adds a group member.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(security): address group RLS review findings

Tighten regression tests for admin-only group visibility, update pgTAP expectations, add private groups API coverage, and rethrow quickError from the members handler catch block.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(security): use non-admin pgTAP user for group RLS denial

test_user is demo org super_admin and bypasses the member-only groups policy; use test_user2 instead.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(security): tighten group access test assertions

Assert quickError payload on forbidden private API responses and ensure pooled pg clients are always released during cleanup.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cli): preview QR PNG/web URL and missing app set options (#2584)

* feat(cli): add preview QR PNG/web URL and missing app set options

Expose console app settings and richer preview output from the CLI so teams can enable preview, print web URLs, save QR PNGs, and manage download channels without the dashboard.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): trim preview-subdomain exports and remove unused import

Keeps knip and oxlint clean after adding CLI preview web URL helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): escape hostname regex in app set test for CodeQL

CodeQL flags unescaped dots in /apps.apple.com/ as an incomplete hostname regexp.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): address PR review feedback on store URL, PNG output, docs

Use exact hostname matching, create parent dirs for --png output, and escape MDX path placeholders.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(cli): update generated docs

* chore(release): 12.184.1

* chore(release): 8.19.0

* fix(db): use RBAC checks in group RLS migration after user_min_right drop

Main's group member-only RLS migration cast to user_min_right, which is
removed by the RBAC hardening migration that runs earlier on this branch.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(test): update groups-rls test for RBAC-only schema

Remove legacy use_new_rbac and user_right columns dropped by the RBAC
hardening migration so groups RLS regression tests run against current schema.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@cursor

cursor Bot commented Jun 29, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_93737fce-6431-48ad-9b2f-ae4653aaa70a)

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants