Skip to content

[codex] fix hashed API key RLS auth bypass#1948

Merged
riderx merged 11 commits into
mainfrom
codex/fix-hashed-apikey-rls-bypass
Apr 27, 2026
Merged

[codex] fix hashed API key RLS auth bypass#1948
riderx merged 11 commits into
mainfrom
codex/fix-hashed-apikey-rls-bypass

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Apr 24, 2026

Summary (AI generated)

  • enforce hashed API key policy inside the shared find_apikey_by_value() auth path used by PostgREST/RLS
  • add a regression helper and focused tests covering plaintext capgkey rejection on the anon/RLS plane
  • keep hashed API keys working while legacy plaintext keys are blocked once the user belongs to an org with enforce_hashed_api_keys=true

Motivation (AI generated)

Repository security advisory GHSA-6g74-8cpq-g2c8 showed that hashed API key enforcement only applied on one auth plane. Direct PostgREST/RLS requests could still authenticate with a legacy plaintext API key through the shared lookup path, which bypassed the intended org security control.

Business Impact (AI generated)

This closes a high-severity authentication bypass on the direct Supabase/PostgREST plane. Organizations that enable hashed API key enforcement now get consistent behavior across both backend and RLS access paths, reducing the chance of silent policy bypass in production.

Test Plan (AI generated)

  • bun lint
  • bunx eslint tests/hashed-apikey-rls.test.ts
  • bun run supabase:db:reset
  • bun run supabase:with-env -- bunx vitest run tests/hashed-apikey-rls.test.ts -t "enforce_hashed_api_keys blocks plaintext capgkey auth on the RLS plane" --reporter verbose --testTimeout 30000

Generated with AI

Summary by CodeRabbit

  • New Features

    • Organizations can enforce hashed-only API keys, blocking plaintext key lookups and access where enforced.
    • App ownership transfers now follow an explicit, audited transfer flow with permission checks and a minimum 32-day interval.
  • Tests

    • Expanded end-to-end tests for hashed API key enforcement, invite vs. role-based membership, enforcement toggles, transfer flows, and improved channel-update resilience and auth header handling.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

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

Adds DB functions to locate API keys and enforce org-level "hashed only" policy during RLS identity lookups, adds guarded app-owner transfer logic with permission checks and cooldown, and adds/updates tests exercising hashed-api-key RLS behavior and a test auth-header fix.

Changes

Cohort / File(s) Summary
Hashed-API-key SQL migration
supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql
Adds public.check_apikey_hashed_key_enforcement(apikey_row public.apikeys) RETURNS boolean (SECURITY DEFINER, REVOKE PUBLIC) to evaluate reachable orgs/role paths for enforce_hashed_api_keys and log denials; adds public.find_apikey_by_value(key_value text) RETURNS SETOF public.apikeys (SECURITY DEFINER, REVOKE PUBLIC, GRANT service_role) that matches plaintext or SHA-256 hex key_hash and filters via the enforcement function.
App transfer SQL migration
supabase/migrations/20260427092702_fix_transfer_app_guard_allowlist.sql
Adds trigger function public.guard_owner_org_reassignment() and SECURITY DEFINER public.transfer_app(p_app_id, p_new_org_id) that enforces caller auth, RBAC checks for source/target orgs, 32-day cooldown via transfer_history, temporarily sets capgo.allow_owner_org_transfer for cascading updates, updates related tables, appends transfer history entry, and logs/rejects unauthorized attempts.
Hashed API Key RLS Tests
tests/hashed-apikey-rls.test.ts
Introduces execAsRoleWithCapgkey, many DB fixture helpers to create org/membership/RBAC/apikey scenarios, and a comprehensive test suite validating plaintext vs hashed key behavior across find/get_identity, direct table reads, RPC paths, invite/pending membership edge-cases, RBAC-derived enforcement, and ignoring stale app-scoped org_id.
Organization API Tests
tests/organization-api.test.ts
Switches PUT /organization auth to per-credential headers via getAuthHeadersForCredentials(...), adds user2AuthHeaders setup and runtime checks, and updates test identities/params to use the second user.
Channel update tests
tests/updates.test.ts
Changes updateChannel to read response as text and throw on non-200 with details; adds postUpdateAfterChannelMutation retry helper that handles transient 429 (with special-case body); updates tests to mutate channels via Supabase updates and use the retry helper.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant RLS as RLS Identity Path
    participant Finder as find_apikey_by_value
    participant Enforcer as check_apikey_hashed_key_enforcement
    participant DB as Database

    Client->>RLS: request with API key
    RLS->>Finder: find_apikey_by_value(key)
    Finder->>DB: SELECT FROM public.apikeys WHERE key=key OR key_hash=sha256_hex(key)
    DB-->>Finder: candidate apikey row(s)
    Finder->>Enforcer: check_apikey_hashed_key_enforcement(apikey_row)
    Enforcer->>DB: evaluate org membership/role paths for enforce_hashed_api_keys
    alt enforcement triggered (plaintext present for enforced org)
        DB-->>Enforcer: enforcement=true
        Enforcer->>DB: pg_log denial (deny: ORG_REQUIRES_HASHED_API_KEY, apikey_id, user_id)
        Enforcer-->>Finder: false (reject row)
        Finder-->>RLS: no matching apikey
        RLS-->>Client: auth rejected
    else enforcement not triggered or hashed key used
        Enforcer-->>Finder: true (allow row)
        Finder-->>RLS: apikey row returned
        RLS-->>Client: auth accepted
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐇 I hop through rows where secrets hide,

I sniff the hash and check the tide.
If orgs demand the hashed key’s art,
I thump my paw and do my part.
🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix hashed API key RLS auth bypass' accurately and specifically describes the main change—closing a high-severity authentication bypass where plaintext API keys could bypass hashed-key enforcement on the RLS plane.
Description check ✅ Passed The PR description is largely complete with a clear Summary, Motivation, Business Impact, and Test Plan sections, though the description template's Screenshots and Checklist sections are not filled out, which are appropriate to skip for backend-only changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-hashed-apikey-rls-bypass

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

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented Apr 24, 2026

Merging this PR will not alter performance

✅ 28 untouched benchmarks


Comparing codex/fix-hashed-apikey-rls-bypass (e43063d) with main (9b7ddc1)

Open in CodSpeed

@riderx riderx marked this pull request as ready for review April 24, 2026 10:06
Copy link
Copy Markdown

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

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: 2e0a02a15e

ℹ️ 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".

Copy link
Copy Markdown

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

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: 93577dc063

ℹ️ 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".

@riderx
Copy link
Copy Markdown
Member Author

riderx commented Apr 24, 2026

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql (1)

50-67: ⚠️ Potential issue | 🟠 Major

Lock down find_apikey_by_value() explicitly.

This helper stays SECURITY DEFINER, but this migration only resets the owner. Please also make the EXECUTE ACL explicit here instead of relying on whatever grants happened to exist before: REVOKE ALL ... FROM PUBLIC, then grant only the roles that should be able to call it.

As per coding guidelines, **/*.sql: For PostgreSQL RPC and helper functions, apply minimum privileges explicitly: start from deny-by-default, set OWNER explicitly, use REVOKE ALL ... FROM PUBLIC, and grant only required roles.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql`
around lines 50 - 67, The migration updates owner but doesn't explicitly set the
function's execution ACL; update the SQL for function
"public"."find_apikey_by_value"(key_value text) to revoke all privileges from
PUBLIC and then grant EXECUTE only to the specific roles that should call it
(e.g., the service role and any app role), i.e. add REVOKE ALL ON FUNCTION
"public"."find_apikey_by_value"(text) FROM PUBLIC; followed by GRANT EXECUTE ON
FUNCTION "public"."find_apikey_by_value"(text) TO <allowed_role(s)> so the
function remains SECURITY DEFINER with a deny-by-default ACL and only the
minimum required roles can invoke find_apikey_by_value.
🧹 Nitpick comments (1)
tests/hashed-apikey-rls.test.ts (1)

365-396: Avoid toggling ORG_ID_RLS in-place for this suite.

This block mutates a shared seeded org in beforeAll and again inside the pending-invite test. If the suite fails before cleanup, later tests inherit the wrong enforce_hashed_api_keys state. Prefer a dedicated org/member seed for this suite and toggle that instead.

As per coding guidelines, tests/**/*.{ts,js}: When creating tests that interact with shared resources, create dedicated seed data when your test modifies resources; reuse existing seed data only when you read without modification and create your own child resources.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/hashed-apikey-rls.test.ts` around lines 365 - 396, The test suite
mutates the shared seeded org (ORG_ID_RLS) in beforeAll/afterAll via
setOrgHashedApiKeyEnforcement which can leave global state dirty if the suite
fails; instead create and use a dedicated test org/member for this suite (e.g.,
seed a new org before tests and remove it after) and toggle enforcement on that
org. Update beforeAll to call a helper like createTestOrg/seedOrgForTests, use
its returned id in createHashedApiKey/createPlainApiKey and in
setOrgHashedApiKeyEnforcement, and in afterAll delete the test org and keys via
deleteApiKey and a corresponding deleteTestOrg/cleanup helper to avoid touching
ORG_ID_RLS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql`:
- Around line 50-67: The migration updates owner but doesn't explicitly set the
function's execution ACL; update the SQL for function
"public"."find_apikey_by_value"(key_value text) to revoke all privileges from
PUBLIC and then grant EXECUTE only to the specific roles that should call it
(e.g., the service role and any app role), i.e. add REVOKE ALL ON FUNCTION
"public"."find_apikey_by_value"(text) FROM PUBLIC; followed by GRANT EXECUTE ON
FUNCTION "public"."find_apikey_by_value"(text) TO <allowed_role(s)> so the
function remains SECURITY DEFINER with a deny-by-default ACL and only the
minimum required roles can invoke find_apikey_by_value.

---

Nitpick comments:
In `@tests/hashed-apikey-rls.test.ts`:
- Around line 365-396: The test suite mutates the shared seeded org (ORG_ID_RLS)
in beforeAll/afterAll via setOrgHashedApiKeyEnforcement which can leave global
state dirty if the suite fails; instead create and use a dedicated test
org/member for this suite (e.g., seed a new org before tests and remove it
after) and toggle enforcement on that org. Update beforeAll to call a helper
like createTestOrg/seedOrgForTests, use its returned id in
createHashedApiKey/createPlainApiKey and in setOrgHashedApiKeyEnforcement, and
in afterAll delete the test org and keys via deleteApiKey and a corresponding
deleteTestOrg/cleanup helper to avoid touching ORG_ID_RLS.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5281ffe4-0edf-42c5-95ef-10a3ae7d6213

📥 Commits

Reviewing files that changed from the base of the PR and between 633aeea and 93577dc.

📒 Files selected for processing (3)
  • supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql
  • tests/hashed-apikey-rls.test.ts
  • tests/organization-api.test.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql`:
- Around line 139-155: The new SECURITY DEFINER helper
find_apikey_by_value(text) must be locked down: after creating/altering the
function, explicitly REVOKE ALL ON FUNCTION public.find_apikey_by_value(text)
FROM PUBLIC and then GRANT EXECUTE ON FUNCTION public.find_apikey_by_value(text)
only to the minimal role(s) that need it (e.g., the DB owner/service role such
as postgres or your application service role); keep the OWNER as set and ensure
the revoke/grant appear in the same migration so the function is not publicly
executable by default.

In `@tests/hashed-apikey-rls.test.ts`:
- Around line 418-444: The test is mutating the shared ORG_ID_RLS fixture;
instead create and use a dedicated org fixture for this suite: replace uses of
ORG_ID_RLS in the beforeAll/afterAll block and calls to
setOrgHashedApiKeyEnforcement with a new org created via the project’s org
creation helper (e.g. createOrg or createTestOrg), store its id in a local
variable (e.g. testOrgId) and tear it down in afterAll; ensure
createHashedApiKey/createPlainApiKey and deleteApiKey are called against that
org’s context if required, and restore/remove the org instead of toggling the
shared org’s enforce_hashed_api_keys flag.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f109669d-eaa3-4d0e-ab7a-e392540632d8

📥 Commits

Reviewing files that changed from the base of the PR and between 93577dc and c1e607d.

📒 Files selected for processing (2)
  • supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql
  • tests/hashed-apikey-rls.test.ts

Comment thread tests/hashed-apikey-rls.test.ts
Copy link
Copy Markdown

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

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: c1e607d85c

ℹ️ 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".

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@supabase/migrations/20260427092702_fix_transfer_app_guard_allowlist.sql`:
- Around line 131-160: The call to PERFORM
set_config('capgo.allow_owner_org_transfer','true',true) in transfer_app() leaks
to the surrounding transaction and can let guard_owner_org_reassignment() be
bypassed; fix by scoping the allowlist change inside a nested PL/pgSQL block so
you always reset it before leaving the block: move the PERFORM
set_config('capgo.allow_owner_org_transfer','true',true) into a BEGIN ... END
block and add an EXCEPTION handler that PERFORMs
set_config('capgo.allow_owner_org_transfer','false',true) and re-raises, and
also PERFORM the 'false' reset after the block on the normal path so the flag is
always cleared even on error. Ensure you reference the existing PERFORM
set_config call and transfer_app()/guard_owner_org_reassignment() when making
the change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f4f93d1-84c0-40bc-98c0-ecc76eda2b71

📥 Commits

Reviewing files that changed from the base of the PR and between c1e607d and 1151eee.

📒 Files selected for processing (1)
  • supabase/migrations/20260427092702_fix_transfer_app_guard_allowlist.sql

Comment thread supabase/migrations/20260427092702_fix_transfer_app_guard_allowlist.sql Outdated
Copy link
Copy Markdown

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

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: 3618b6a0d2

ℹ️ 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".

Copy link
Copy Markdown

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

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: 941b047a48

ℹ️ 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".

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql (1)

42-42: Fully qualify built-ins under the empty search_path.

This migration still calls now(), jsonb_build_object(), and encode() unqualified. With SET search_path TO '', please make these pg_catalog.now(), pg_catalog.jsonb_build_object(), and pg_catalog.encode() to match the repository rule.

As per coding guidelines: **/*.sql: Set an empty search path (search_path = '') in every PostgreSQL function and use fully qualified names for all references

Also applies to: 59-59, 69-69, 79-79, 97-97, 108-108, 119-119, 138-138, 150-150, 159-159, 182-182

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql`
at line 42, The migration uses unqualified built-ins under an empty search_path:
replace calls to now(), jsonb_build_object(), and encode() with fully-qualified
pg_catalog.now(), pg_catalog.jsonb_build_object(), and pg_catalog.encode()
respectively (e.g., inside the RLS/identity SQL snippets and any function bodies
referenced by names in the diff) so all built-in references are fully qualified;
apply the same replacements for every other occurrence of these built-ins in the
file (the other instances noted in the review) to comply with the repository
rule requiring search_path = ''.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql`:
- Around line 188-190: The migration currently revokes public access and grants
"find_apikey_by_value(text)" only to service_role, which will break direct
RPC/SQL calls from Edge Functions (supabase/functions/_backend/utils/supabase.ts
uses .rpc('find_apikey_by_value', ...) and
supabase/functions/_backend/utils/hono_middleware.ts runs SELECT * FROM
find_apikey_by_value(...)); fix by either granting the function to the
authenticated/anon roles in the migration (add GRANT EXECUTE ON FUNCTION
"public"."find_apikey_by_value"(text) TO authenticated, anon) or convert the
function to a SECURITY DEFINER wrapper owned by service_role and keep
restrictive grants so callers invoke the wrapper or RPC without permission
errors (ensure wrapper name and callers are updated accordingly).

---

Nitpick comments:
In
`@supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql`:
- Line 42: The migration uses unqualified built-ins under an empty search_path:
replace calls to now(), jsonb_build_object(), and encode() with fully-qualified
pg_catalog.now(), pg_catalog.jsonb_build_object(), and pg_catalog.encode()
respectively (e.g., inside the RLS/identity SQL snippets and any function bodies
referenced by names in the diff) so all built-in references are fully qualified;
apply the same replacements for every other occurrence of these built-ins in the
file (the other instances noted in the review) to comply with the repository
rule requiring search_path = ''.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d429f874-b557-4959-a050-aff197a6f893

📥 Commits

Reviewing files that changed from the base of the PR and between 1151eee and 941b047.

📒 Files selected for processing (3)
  • supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql
  • supabase/migrations/20260427092702_fix_transfer_app_guard_allowlist.sql
  • tests/hashed-apikey-rls.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/hashed-apikey-rls.test.ts
  • supabase/migrations/20260427092702_fix_transfer_app_guard_allowlist.sql

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
tests/organization-api.test.ts (1)

48-60: ⚠️ Potential issue | 🟠 Major

Keep ORG_ID accessible to the default test user to avoid cross-suite regressions

At Line 48 and Line 59, the seed now grants ORG_ID ownership/membership only to USER_ID_2, but many existing tests in this file still operate on ORG_ID using headers/USER_ID assumptions. This can turn unrelated tests into auth failures.

Suggested fix
   const { error: orgUserError } = await getSupabaseClient().from('org_users').insert({
-    org_id: ORG_ID,
-    user_id: USER_ID_2,
-    user_right: 'super_admin',
-  })
+    org_id: ORG_ID,
+    user_id: USER_ID_2,
+    user_right: 'super_admin',
+  })
+  if (orgUserError)
+    throw orgUserError
+
+  // Keep legacy organization API tests (USER_ID/headers) valid on this shared org seed
+  const { error: orgUserErrorUser1 } = await getSupabaseClient().from('org_users').insert({
+    org_id: ORG_ID,
+    user_id: USER_ID,
+    user_right: 'super_admin',
+  })
+  if (orgUserErrorUser1)
+    throw orgUserErrorUser1
-  if (orgUserError)
-    throw orgUserError
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/organization-api.test.ts` around lines 48 - 60, The seed grants ORG_ID
only to USER_ID_2 which breaks tests that use the default test user (USER_ID);
update the seeding so the default test user retains access: in the org
creation/seed block (the code that sets created_by and inserts into org_users
via getSupabaseClient().from('org_users').insert) also insert or upsert an
org_users row for USER_ID with appropriate user_right (e.g., 'super_admin' or
same right as USER_ID_2) or set created_by to USER_ID; ensure ORG_ID, USER_ID,
and USER_ID_2 remain referenced so existing tests using headers/USER_ID still
authenticate against ORG_ID.
🧹 Nitpick comments (1)
tests/updates.test.ts (1)

85-87: Make transient-429 detection schema-based instead of string-match

The retry guard is currently brittle to JSON formatting differences. Parse once and check error === 'on_premise_app' to avoid false negatives.

Suggested patch
-    const responseText = await response.text()
-    if (!responseText.includes('"error":"on_premise_app"')) {
+    const responseText = await response.text()
+    let errorCode: string | undefined
+    try {
+      errorCode = (JSON.parse(responseText) as { error?: string }).error
+    }
+    catch {
+      errorCode = undefined
+    }
+    if (errorCode !== 'on_premise_app') {
       throw new Error(`Unexpected update failure after channel mutation (${response.status}): ${responseText}`)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/updates.test.ts` around lines 85 - 87, Replace the brittle string-match
check against responseText with a schema-based check: JSON.parse the
responseText (or use await response.json() if available) and assert that the
parsed object's error property === 'on_premise_app'; if parsing fails, fall back
to the existing behavior that throws the Error with response.status and
responseText. Update the assertion in tests/updates.test.ts (the block
referencing responseText and response.status) to perform the parsed-object check
so transient-429 detection doesn’t depend on JSON formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@tests/organization-api.test.ts`:
- Around line 48-60: The seed grants ORG_ID only to USER_ID_2 which breaks tests
that use the default test user (USER_ID); update the seeding so the default test
user retains access: in the org creation/seed block (the code that sets
created_by and inserts into org_users via
getSupabaseClient().from('org_users').insert) also insert or upsert an org_users
row for USER_ID with appropriate user_right (e.g., 'super_admin' or same right
as USER_ID_2) or set created_by to USER_ID; ensure ORG_ID, USER_ID, and
USER_ID_2 remain referenced so existing tests using headers/USER_ID still
authenticate against ORG_ID.

---

Nitpick comments:
In `@tests/updates.test.ts`:
- Around line 85-87: Replace the brittle string-match check against responseText
with a schema-based check: JSON.parse the responseText (or use await
response.json() if available) and assert that the parsed object's error property
=== 'on_premise_app'; if parsing fails, fall back to the existing behavior that
throws the Error with response.status and responseText. Update the assertion in
tests/updates.test.ts (the block referencing responseText and response.status)
to perform the parsed-object check so transient-429 detection doesn’t depend on
JSON formatting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9183f82e-80d1-4174-8a85-c891a802ef46

📥 Commits

Reviewing files that changed from the base of the PR and between 941b047 and e488e01.

📒 Files selected for processing (4)
  • supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql
  • tests/hashed-apikey-rls.test.ts
  • tests/organization-api.test.ts
  • tests/updates.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/hashed-apikey-rls.test.ts
  • supabase/migrations/20260424091645_enforce_hashed_api_keys_on_rls_identity_path.sql

Copy link
Copy Markdown

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

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: e488e01ab7

ℹ️ 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 tests/organization-api.test.ts
@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx merged commit 62ba425 into main Apr 27, 2026
16 checks passed
@riderx riderx deleted the codex/fix-hashed-apikey-rls-bypass branch April 27, 2026 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant