Skip to content

Restrict API key oracle RPC access#1793

Open
riderx wants to merge 28 commits intomainfrom
riderx/fix-rpc-oracle-perms
Open

Restrict API key oracle RPC access#1793
riderx wants to merge 28 commits intomainfrom
riderx/fix-rpc-oracle-perms

Conversation

@riderx
Copy link
Member

@riderx riderx commented Mar 13, 2026

Summary\n\n- Restrict API key oracle RPC access so / are service-role only, with explicit unauthenticated/authenticated denial.\n- Fix to validate key ownership and app existence before granting access.\n- Replace apps storage bucket RLS policy expressions to avoid oracle lookups with /.\n- Add pgTAP coverage for oracle permission behavior across anonymous, authenticated, and service-role roles.

Summary by CodeRabbit

  • New Features

    • Added API-key + app validation to control app-level operations.
  • Bug Fixes

    • Prevented anonymous/regular users from invoking service-only privileged actions.
    • Strengthened storage policies to enforce app/folder ownership for read/write/delete.
  • Tests

    • Expanded test coverage for anonymous, authenticated, and service-role permission scenarios; test setup now includes service-role authentication where needed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 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 a new public function is_allowed_action(apikey, appid), tightens EXECUTE grants on API-key oracle RPCs to service_role, replaces legacy apps-bucket RLS with folder-based storage.objects policies for the apps bucket, and adds/updates tests to run assertions under service-role, anonymous, and authenticated contexts.

Changes

Cohort / File(s) Summary
Access control & RLS
supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql
Revokes EXECUTE for anon/public on public.get_user_id (both variants) and public.get_org_perm_for_apikey; grants to service_role; adds public.is_allowed_action(apikey, appid); replaces legacy apps-bucket policies with folder-based storage.objects policies (DELETE/UPDATE/INSERT/SELECT) enforcing auth.uid or API-key ownership via helper checks.
Test adjustments (existing files)
supabase/tests/07_auth_functions.sql, supabase/tests/10_utility_functions.sql, supabase/tests/19_test_identity_functions.sql
Inserts tests.authenticate_as_service_role() calls so subsequent test assertions execute with service-role privileges.
New tests — API-key oracle permissions
supabase/tests/45_test_apikey_oracle_permissions.sql
Adds an 8-test suite validating anon/authenticated/service-role behaviors for get_user_id (with/without app_id) and get_org_perm_for_apikey, asserting permission errors for anon and expected values for authenticated/service-role contexts.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Func as is_allowed_action(apikey, appid)
    participant Auth as ServiceRole/Auth
    participant DB as Database
    participant Storage as storage.objects

    Client->>Func: call is_allowed_action(apikey, appid)
    Func->>Auth: validate API key / resolve identity
    Auth-->>Func: identity (user_id, key_mode)
    alt API key valid
        Func->>DB: lookup app -> org_id
        DB-->>Func: return org_id
        Func->>Auth: check org ownership/permissions
        Auth-->>Func: allowed? (true/false)
        Func->>Storage: evaluated by storage policies (auth.uid or apikey ownership)
        Storage-->>Func: access permitted?
        Func-->>Client: return allowed boolean
    else invalid key
        Func-->>Client: return false
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐇 I nibble at SQL gates with twitchy paws,

Keys checked, the burrow bolted without pause.
Service-role sentry hums a steady tune,
Folders locked safe beneath the moon.
A hop, a cheer — secure code blooms soon!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers the main objectives but lacks a Test plan section and Screenshots section as specified in the template. Add a Test plan section detailing steps to verify the RLS policy changes and oracle permission behavior, and clarify if screenshots are needed for backend changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: restricting API key oracle RPC access to service-role only with explicit access denial.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 riderx/fix-rpc-oracle-perms

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

Copy link
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/tests/45_test_apikey_oracle_permissions.sql (1)

57-71: Consider testing the two-argument get_user_id overload for service_role.

The service_role tests verify get_user_id(apikey) but not get_user_id(apikey, app_id). Adding this would complete the coverage symmetry with the denial tests.

🧪 Suggested additional test
 SELECT
     is(
         get_user_id('ae6e7458-c46d-4c00-aa3b-153b0b8520ea'),
         '6aa76066-55ef-4238-ade6-0b32334a4097'::uuid,
         'service role should still call get_user_id'
     );

+SELECT
+    is(
+        get_user_id('ae6e7458-c46d-4c00-aa3b-153b0b8520ea', 'com.demo.app'),
+        '6aa76066-55ef-4238-ade6-0b32334a4097'::uuid,
+        'service role should still call get_user_id with app_id'
+    );
+
 SELECT
     is(
         get_org_perm_for_apikey('ae6e7458-c46d-4c00-aa3b-153b0b8520ea', 'com.demo.app'),

Update the plan to SELECT plan(9); accordingly.

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

In `@supabase/tests/45_test_apikey_oracle_permissions.sql` around lines 57 - 71,
Add a test that calls the two-argument overload get_user_id(apikey, app_id)
under the service_role context to mirror the existing single-argument check and
ensure symmetry with the denial tests; specifically, after
authenticate_as_service_role(), add an is(...) assertion that compares
get_user_id('ae6e7458-c46d-4c00-aa3b-153b0b8520ea','com.demo.app') to the
expected UUID (same expected value used for the single-arg test) and a
descriptive message, and update the test plan count to SELECT plan(9); so the
new test is accounted for.
🤖 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/20260313112417_restrict-apikey-oracle-rls-fixes.sql`:
- Around line 20-47: The is_allowed_action function validates API keys but lacks
the matching permission lockdown; after the is_allowed_action ("apikey" "text",
"appid" "text") function definition add explicit REVOKE ALL for anon,
authenticated, and public and then GRANT EXECUTE to service_role (mirroring the
pattern used for get_user_id and get_org_perm_for_apikey) so only service_role
can call this sensitive function.

---

Nitpick comments:
In `@supabase/tests/45_test_apikey_oracle_permissions.sql`:
- Around line 57-71: Add a test that calls the two-argument overload
get_user_id(apikey, app_id) under the service_role context to mirror the
existing single-argument check and ensure symmetry with the denial tests;
specifically, after authenticate_as_service_role(), add an is(...) assertion
that compares get_user_id('ae6e7458-c46d-4c00-aa3b-153b0b8520ea','com.demo.app')
to the expected UUID (same expected value used for the single-arg test) and a
descriptive message, and update the test plan count to SELECT plan(9); so the
new test is accounted for.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 61cb4981-9877-4605-a6ac-e19441864159

📥 Commits

Reviewing files that changed from the base of the PR and between 2b76901 and e627f12.

📒 Files selected for processing (5)
  • supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql
  • supabase/tests/07_auth_functions.sql
  • supabase/tests/10_utility_functions.sql
  • supabase/tests/19_test_identity_functions.sql
  • supabase/tests/45_test_apikey_oracle_permissions.sql

@riderx
Copy link
Member Author

riderx commented Mar 16, 2026

CI check failed on March 16, 2026. The failing case is , which errors with . I did not apply a quick fix here because the same failure signature is also present on other Riderx PRs and looks like a shared/base-branch permission regression rather than something isolated to this branch.

@riderx
Copy link
Member Author

riderx commented Mar 16, 2026

CI check Run tests failed on March 16, 2026. The failing case is supabase/tests/45_test_shared_public_images.sql, which errors with permission denied for function get_identity_apikey_only. I did not apply a quick fix here because the same failure signature is also present on other Riderx PRs and looks like a shared/base-branch permission regression rather than something isolated to this branch.

Copy link
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.

♻️ Duplicate comments (1)
supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql (1)

24-51: ⚠️ Potential issue | 🟠 Major

Lock down public.is_allowed_action(text, text) execute privileges.

SECURITY DEFINER was updated at Line 24, but this migration does not explicitly revoke/grant execute for this function. Please apply the same service-role-only ACL pattern used above.

Suggested patch
 CREATE OR REPLACE FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") RETURNS boolean
@@
 $$;
+
+REVOKE ALL ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") FROM "anon";
+REVOKE ALL ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") FROM "authenticated";
+REVOKE ALL ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") FROM "public";
+GRANT EXECUTE ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") TO "service_role";

As per coding guidelines: “Apply REVOKE ALL ... FROM PUBLIC to every PostgreSQL function overload and grant only required roles (e.g., service_role, authenticated)”.

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

In `@supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql`
around lines 24 - 51, The new SECURITY DEFINER function
public.is_allowed_action(apikey text, appid text) lacks explicit ACL changes;
update the migration to revoke all exec privileges from PUBLIC for the
is_allowed_action(text,text) overload and then grant EXECUTE only to the service
role (and other permitted roles per pattern, e.g., authenticated) so the
function follows the service-role-only ACL pattern used elsewhere; reference the
function by its exact signature public.is_allowed_action(text,text) when adding
the REVOKE ALL ON FUNCTION ... FROM PUBLIC and the subsequent GRANT EXECUTE ON
FUNCTION ... TO service_role (and any other allowed roles).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql`:
- Around line 24-51: The new SECURITY DEFINER function
public.is_allowed_action(apikey text, appid text) lacks explicit ACL changes;
update the migration to revoke all exec privileges from PUBLIC for the
is_allowed_action(text,text) overload and then grant EXECUTE only to the service
role (and other permitted roles per pattern, e.g., authenticated) so the
function follows the service-role-only ACL pattern used elsewhere; reference the
function by its exact signature public.is_allowed_action(text,text) when adding
the REVOKE ALL ON FUNCTION ... FROM PUBLIC and the subsequent GRANT EXECUTE ON
FUNCTION ... TO service_role (and any other allowed roles).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52bfb2d6-8dcb-4e69-88f7-c5526591f107

📥 Commits

Reviewing files that changed from the base of the PR and between bba2a4e and 64615ef.

📒 Files selected for processing (1)
  • supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql

Copy link
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.

🧹 Nitpick comments (1)
supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql (1)

50-77: Consider caching get_apikey_header() to avoid duplicate subquery evaluation.

The get_apikey_header() is invoked twice within the OR branch (lines 63-65 and 69-71). While PostgreSQL may apply common subexpression elimination, explicitly caching the value would be clearer and ensure consistent behavior across PostgreSQL versions.

♻️ Alternative pattern using a single subquery reference
-            OR (
-                public.get_user_id(
-                    (
-                        SELECT
-                            "public"."get_apikey_header" ()
-                    )
-                )::text = ("storage"."foldername" ("name")) [0]
-                AND public.is_allowed_capgkey(
-                    (
-                        SELECT
-                            "public"."get_apikey_header" ()
-                    ),
-                    '{all}'::"public"."key_mode" [],
-                    ("storage"."foldername" ("name")) [1]
-                )
-            )
+            OR (
+                SELECT
+                    public.get_user_id(_apikey)::text = ("storage"."foldername" ("name")) [0]
+                    AND public.is_allowed_capgkey(
+                        _apikey,
+                        '{all}'::"public"."key_mode" [],
+                        ("storage"."foldername" ("name")) [1]
+                    )
+                FROM (SELECT "public"."get_apikey_header"() AS _apikey) AS _header
+            )

This pattern applies to all four policies (DELETE, UPDATE, INSERT, SELECT).

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

In `@supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql`
around lines 50 - 77, The policy calls public.get_apikey_header() twice; compute
it once and reuse it to avoid duplicated subquery evaluation and ensure
consistent results. Replace the OR branch that invokes get_apikey_header() with
a single WITH subquery that selects public.get_apikey_header() AS apikey (e.g.
WITH apikey AS (SELECT public.get_apikey_header() AS val) SELECT ... FROM
apikey) and then use that apikey value for both public.get_user_id(...) and
public.is_allowed_capgkey(...); update the USING expression in the policy (named
"Allow user or apikey to delete they own folder in apps") to reference this
single apikey value instead of calling get_apikey_header() twice.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql`:
- Around line 50-77: The policy calls public.get_apikey_header() twice; compute
it once and reuse it to avoid duplicated subquery evaluation and ensure
consistent results. Replace the OR branch that invokes get_apikey_header() with
a single WITH subquery that selects public.get_apikey_header() AS apikey (e.g.
WITH apikey AS (SELECT public.get_apikey_header() AS val) SELECT ... FROM
apikey) and then use that apikey value for both public.get_user_id(...) and
public.is_allowed_capgkey(...); update the USING expression in the policy (named
"Allow user or apikey to delete they own folder in apps") to reference this
single apikey value instead of calling get_apikey_header() twice.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c44454c-cfc7-43a5-a8af-9b58477a86a9

📥 Commits

Reviewing files that changed from the base of the PR and between 64615ef and 411ee3c.

📒 Files selected for processing (2)
  • supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql
  • supabase/tests/45_test_apikey_oracle_permissions.sql
🚧 Files skipped from review as they are similar to previous changes (1)
  • supabase/tests/45_test_apikey_oracle_permissions.sql

Copy link
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

♻️ Duplicate comments (1)
supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql (1)

20-47: ⚠️ Potential issue | 🟠 Major

is_allowed_action(text,text) still needs explicit privilege lockdown.

CREATE OR REPLACE FUNCTION does not reset existing grants. Without explicit REVOKE/GRANT statements after this definition, prior broader EXECUTE permissions can persist.

🔒 Proposed grant fix
+REVOKE ALL ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") FROM "anon";
+REVOKE ALL ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") FROM "authenticated";
+REVOKE ALL ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") FROM "public";
+GRANT EXECUTE ON FUNCTION "public"."is_allowed_action" ("apikey" "text", "appid" "text") TO "service_role";
As per coding guidelines, "Apply `REVOKE ALL ... FROM PUBLIC` to every PostgreSQL function overload and grant only required roles (e.g., `service_role`, `authenticated`)."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql`
around lines 20 - 47, The function public.is_allowed_action(text,text) was
redefined but existing EXECUTE grants aren’t reset; after the CREATE OR REPLACE
FUNCTION block add explicit privilege lockdown: REVOKE ALL ON FUNCTION
public.is_allowed_action(text,text) FROM PUBLIC and then GRANT only the minimal
roles that need execute (e.g., service_role and authenticated) — ensure you
reference the exact overload name public.is_allowed_action(text,text) when
revoking/granting so prior broader permissions are removed and only the intended
roles retain EXECUTE.
🤖 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/20260313112417_restrict-apikey-oracle-rls-fixes.sql`:
- Around line 58-59: RLS policies compare auth.uid() to elements of the array
returned by the function "storage"."foldername"("name") but use 0-based indexing
which yields NULL; update every occurrence of ("storage"."foldername"("name"))
[0] used in user_id equality checks to use 1-based indexing
("storage"."foldername"("name")) [1] instead (affecting the comparisons that
involve ("auth"."uid"()) = ("storage"."foldername"("name"))[0]); ensure you
change all instances referenced in the comment so the policies correctly compare
("auth"."uid"()) to the first array element.

---

Duplicate comments:
In `@supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql`:
- Around line 20-47: The function public.is_allowed_action(text,text) was
redefined but existing EXECUTE grants aren’t reset; after the CREATE OR REPLACE
FUNCTION block add explicit privilege lockdown: REVOKE ALL ON FUNCTION
public.is_allowed_action(text,text) FROM PUBLIC and then GRANT only the minimal
roles that need execute (e.g., service_role and authenticated) — ensure you
reference the exact overload name public.is_allowed_action(text,text) when
revoking/granting so prior broader permissions are removed and only the intended
roles retain EXECUTE.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4fe18c86-5c82-4b3e-9467-2b1dc4e02b23

📥 Commits

Reviewing files that changed from the base of the PR and between 411ee3c and aabebb3.

📒 Files selected for processing (1)
  • supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql

@riderx
Copy link
Member Author

riderx commented Mar 17, 2026

CI check Run tests failed again on March 17, 2026. The latest failure signature is now a broad CLI/API-key auth regression: many cases in tests/cli-new-encryption.test.ts, tests/cli-old-checksum.test.ts, and tests/cli.test.ts now return Cannot authenticate user with provided API key instead of the expected success or branch-specific assertion errors.\n\nI did not apply a quick fix here because this no longer looks isolated to the storage RLS change on this branch; it points to a wider break in the API-key auth/oracle path introduced by the hardening changes, and a safe fix likely needs a broader redesign rather than a one-line grant change.

@riderx
Copy link
Member Author

riderx commented Mar 17, 2026

CI triage: Run tests is failing because multiple CLI suites now receive Cannot authenticate user with provided API key instead of the expected domain responses.

The regression points at supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql: API-key oracle RPC execution was tightened, but the current CLI/API auth path still appears to depend on that behavior. This is not a safe one-line fix because the options are either re-opening those RPCs (security tradeoff) or refactoring the auth path to stop depending on anon/oracle execution.

I did not push a speculative patch.

Copy link

@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: abcf767da2

ℹ️ 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 on lines +14 to +16
GRANT EXECUTE ON FUNCTION "public"."get_user_id" ("apikey" "text") TO "authenticated";
GRANT EXECUTE ON FUNCTION "public"."get_user_id" ("apikey" "text", "app_id" "text") TO "authenticated";
GRANT EXECUTE ON FUNCTION "public"."get_org_perm_for_apikey" ("apikey" "text", "app_id" "text") TO "authenticated";

Choose a reason for hiding this comment

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

P1 Badge Revoke authenticated access to API-key oracle RPCs

These grants keep the oracle open to every signed-in user. supabase/tests/45_test_apikey_oracle_permissions.sql:31-52 now codifies that any authenticated account can call get_user_id(...) and get_org_perm_for_apikey(...), which is enough to probe guessed Capgo keys and learn both whether a key is valid and what access it has on a target app. I checked the repo for callers and only found service-side/security-definer usage (supabase/functions/_backend/utils/pg_files.ts:12-27, supabase/schemas/prod.sql:3626, supabase/schemas/prod.sql:6456-6462), so exposing these RPCs to authenticated is unnecessary and defeats the stated restriction.

Useful? React with 👍 / 👎.

Copy link

@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: 88dd2bd127

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

-- ============================================================================
REVOKE ALL ON FUNCTION "public"."get_user_id" ("apikey" "text") FROM "anon";
REVOKE ALL ON FUNCTION "public"."get_user_id" ("apikey" "text", "app_id" "text") FROM "anon";
REVOKE ALL ON FUNCTION "public"."get_org_perm_for_apikey" ("apikey" "text", "app_id" "text") FROM "anon";

Choose a reason for hiding this comment

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

P1 Badge Update remaining pgTAP callers before revoking anon access

Revoking anon execute here breaks the existing backend SQL suite: supabase/tests/14_test_apikey.sql and supabase/tests/24_test_data_functions.sql still call get_org_perm_for_apikey(...) without tests.authenticate_as_service_role() or any authenticated context, so they now hit permission denied for function get_org_perm_for_apikey. This commit updated 07_auth_functions.sql, 10_utility_functions.sql, and 19_test_identity_functions.sql, but these two remaining files were left behind, so bun test:backend will start failing immediately.

Useful? React with 👍 / 👎.

@riderx
Copy link
Member Author

riderx commented Mar 18, 2026

Run tests fails across CLI/API-key paths with Cannot authenticate user with provided API key. The failure is systemic across multiple CLI suites, so it is broader than a PR-local fix.

@riderx
Copy link
Member Author

riderx commented Mar 18, 2026

PR #1793: CI is failing in the auth/RLS path around get_identity_apikey_only / NO_RIGHTS permission expectations. This is not a one-line lint issue; it needs a deeper review of the RPC grants and the tests that assert the current auth behavior.

Copy link

@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: 68929d8466

ℹ️ 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 on lines +73 to +76
FROM "public"."apps" AS "app"
WHERE
"app"."app_id" = ("storage"."foldername" ("name")) [2]
AND "app"."user_id"::text = ("storage"."foldername" ("name")) [1]

Choose a reason for hiding this comment

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

P2 Badge Keep apps-bucket auth out of public.apps RLS

For API-key requests against the apps storage bucket, this new FROM public.apps lookup now runs under the caller's public.apps SELECT policy instead of only using the old security-definer helpers. I checked supabase/schemas/prod.sql:15596, and that policy calls check_min_rights(...); check_min_rights in supabase/schemas/prod.sql:1337-1356 enforces org 2FA/password-policy gates before the row is visible. The previous bucket policy only used get_user_id()/is_allowed_capgkey(), so valid app-owner keys that used to work will now be rejected as soon as the org enables those auth requirements (or otherwise loses app.read visibility). If this migration is only meant to remove the oracle, the app lookup needs to avoid caller-side RLS.

Useful? React with 👍 / 👎.

@riderx
Copy link
Member Author

riderx commented Mar 19, 2026

PR #1793 is failing across the CLI/API-key auth suite, mainly and , where assertions now get . That looks like a shared auth behavior regression rather than a small PR-local bug, so I did not try to force a branch-only patch.

@riderx
Copy link
Member Author

riderx commented Mar 19, 2026

CI is failing in Run tests, but the red job is in tests/cli-meta.test.ts and the SQL coverage in this PR passes. The failure appears unrelated to the migration/test changes in this branch, so this looks blocked by a repo-level test regression rather than a simple local fix. PR link: #1793

@riderx
Copy link
Member Author

riderx commented Mar 19, 2026

CI is still red on https://github.com/Cap-go/capgo/actions/runs/23273324029/job/67670721977. The failure is broad across the CLI/API-key auth suite: Cannot authenticate user with provided API key appears where the tests expect business-level errors like Cannot find channel / Cannot find version. That looks like a shared auth regression, not a small branch-local fix.

@riderx
Copy link
Member Author

riderx commented Mar 19, 2026

CI is failing because the API-key auth path is still calling into the old get_user_id() flow from supabase/functions/_backend/files/files.ts:500 via supabase/functions/_backend/utils/pg_files.ts:12-27, while this PR hardens the oracle functions in supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql:4-16. The result is that valid CLI API keys now fail auth, so tests/cli.test.ts:46-67 and tests/cli-channel.test.ts:65-177 report Cannot authenticate user with provided API key instead of the expected bundle/channel errors. This is a broader auth-path change, not a small SQL-only fix.

@riderx
Copy link
Member Author

riderx commented Mar 19, 2026

I checked #1793. The latest failing Run tests job is failing in tests/cli.test.ts with repeated expected false to be true assertions at lines 200 and 247. This looks like a broader CLI/auth behavior regression, not a safe branch-local fix, so I am not patching it here.

Copy link

@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: b300051ec1

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

@@ -0,0 +1,190 @@
-- ============================================================================

Choose a reason for hiding this comment

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

P1 Badge Rename this migration to a fresh timestamp

I checked .github/workflows/build_and_deploy.yml:62-63, which deploys schema changes with plain supabase db push, and this repository already contains later migrations such as supabase/migrations/20260319164053_fix_manifest_select_rls.sql. Supabase tracks only migration timestamps in supabase_migrations.schema_migrations, and its CLI docs note that older missing files are only replayed when --include-all is used. Because this new file is backdated to 20260313112417, any staging/production database that has already applied the March 17-19 migrations will skip it entirely, so the RPC grant changes and storage policy fixes in this commit never reach deployed environments.

Useful? React with 👍 / 👎.

Copy link

@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: d6de5bed08

ℹ️ 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 on lines +4 to +16
REVOKE ALL ON FUNCTION "public"."get_user_id" ("apikey" "text") FROM "anon";
REVOKE ALL ON FUNCTION "public"."get_user_id" ("apikey" "text", "app_id" "text") FROM "anon";
REVOKE ALL ON FUNCTION "public"."get_org_perm_for_apikey" ("apikey" "text", "app_id" "text") FROM "anon";
REVOKE ALL ON FUNCTION "public"."get_user_id" ("apikey" "text") FROM "public";
REVOKE ALL ON FUNCTION "public"."get_user_id" ("apikey" "text", "app_id" "text") FROM "public";
REVOKE ALL ON FUNCTION "public"."get_org_perm_for_apikey" ("apikey" "text", "app_id" "text") FROM "public";

GRANT EXECUTE ON FUNCTION "public"."get_user_id" ("apikey" "text") TO "service_role";
GRANT EXECUTE ON FUNCTION "public"."get_user_id" ("apikey" "text", "app_id" "text") TO "service_role";
GRANT EXECUTE ON FUNCTION "public"."get_org_perm_for_apikey" ("apikey" "text", "app_id" "text") TO "service_role";
GRANT EXECUTE ON FUNCTION "public"."get_user_id" ("apikey" "text") TO "authenticated";
GRANT EXECUTE ON FUNCTION "public"."get_user_id" ("apikey" "text", "app_id" "text") TO "authenticated";
GRANT EXECUTE ON FUNCTION "public"."get_org_perm_for_apikey" ("apikey" "text", "app_id" "text") TO "authenticated";

Choose a reason for hiding this comment

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

P1 Badge Revoke the remaining API-key ownership oracle RPCs

This migration hardens get_user_id/get_org_perm_for_apikey, but it still leaves the same probe open through other helpers. public.is_app_owner(apikey, appid) remains executable by anon/authenticated (supabase/schemas/prod.sql:17466-17468) and directly wraps public.get_user_id(apikey) (supabase/schemas/prod.sql:6456-6461); public.get_org_owner_id(apikey, app_id) is likewise still public (supabase/schemas/prod.sql:17225-17227, 4540-4563). The updated suite even continues to call is_app_owner(...) anonymously in supabase/tests/19_test_identity_functions.sql:176-184, so unauthenticated callers can still probe guessed keys/apps after this migration.

Useful? React with 👍 / 👎.

@riderx
Copy link
Member Author

riderx commented Mar 20, 2026

PR #1793 is still failing on the broader API-key auth regression, not a single local test. The current CI failures in , , and all stop at , so the blocker is the auth/oracle migration behavior rather than a one-line test tweak. The remaining unresolved review threads on need a broader pass before this can go green.

@riderx
Copy link
Member Author

riderx commented Mar 20, 2026

PR #1793 is still failing on the broader API-key auth regression, not a single local test. The current CI failures in tests/cli-channel.test.ts, tests/cli-hashed-apikey.test.ts, and tests/cli-meta.test.ts all stop at Cannot authenticate user with provided API key, so the blocker is the auth/oracle migration behavior rather than a one-line test tweak. The remaining unresolved review threads on supabase/migrations/20260313112417_restrict-apikey-oracle-rls-fixes.sql need a broader pass before this can go green.

Copy link

@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: 8eadc02126

ℹ️ 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 on lines +67 to +68
(SELECT "auth"."uid" ())::text = ("storage"."foldername" ("name")) [1]
)

Choose a reason for hiding this comment

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

P1 Badge Keep apps-bucket policies on the correct path segments

App artifacts are generated as orgs/{owner_org}/apps/{app_id}/... in supabase/functions/_backend/private/upload_link.ts:59-60, and the upload middleware validates that same shape in supabase/functions/_backend/files/files.ts:440-469. With that layout, storage.foldername(name)[1] is the literal orgs, so this predicate is always false for signed-in users, and the repeated [2] lookups below read the org id instead of the app id. Because the same off-by-one indexing is copied into all four recreated apps-bucket policies, valid read/write/delete/insert requests will start getting rejected after this migration.

Useful? React with 👍 / 👎.

@riderx
Copy link
Member Author

riderx commented Mar 21, 2026

CI is blocked by repo-wide lint failures in src/components/dashboard/InviteTeammateModal.vue:25 and src/pages/onboarding/organization.vue:613-614, which are unrelated to this branch. The remaining live review threads are still the migration/RLS items around oracle RPC grants, apps-bucket auth, migration timestamping, and path handling. #1793

@riderx
Copy link
Member Author

riderx commented Mar 21, 2026

CI is blocked by repo-wide lint failures in #1793. The failing run stops in () and (). I did not patch this branch because the failures are outside the PR diff and need a repo-wide lint fix or rebase.

@riderx
Copy link
Member Author

riderx commented Mar 21, 2026

Correction: CI is blocked by repo-wide lint failures in #1793. The failing run stops in src/components/dashboard/InviteTeammateModal.vue:25 (vue/define-macros-order) and src/pages/onboarding/organization.vue:613-614 (vue/html-indent). I did not patch this branch because the failures are outside the PR diff and need a repo-wide lint fix or rebase.

@sonarqubecloud
Copy link

Copy link

@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: ed86a001d8

ℹ️ 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 on lines +75 to +76
"app"."app_id" = ("storage"."foldername" ("name")) [2]
AND "app"."user_id"::text = ("storage"."foldername" ("name")) [1]

Choose a reason for hiding this comment

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

P1 Badge Use owner_org for apps-bucket ownership checks

upload_link.ts:59-60 and files.ts:447-469 show that app artifacts are stored under orgs/{owner_org}/apps/{app_id}/..., so the recreated apps-bucket policies need to validate against the app's org, not a user UUID. Here the policy compares the path to auth.uid()/app.user_id, which means even after correcting the storage.foldername(...) indexes, valid read/write/delete/insert requests will still be rejected whenever the owner user id differs from the org id (the normal case). The same user-vs-org mismatch is duplicated in all four recreated policies.

Useful? React with 👍 / 👎.

@riderx
Copy link
Member Author

riderx commented Mar 21, 2026

Status update for #1793:

This PR still has 7 unresolved review threads. The latest CI run is red in the shared CLI suite, with failures in tests/cli-new-encryption.test.ts, tests/cli-old-checksum.test.ts, and tests/cli.test.ts.

That is too broad for a safe one-pass fix, so I left the branch unchanged and reported the blocker instead.

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