Skip to content

Harden get_identity_apikey_only RPC permissions#1765

Merged
riderx merged 19 commits into
mainfrom
riderx/fix-attached-issue-2
Mar 16, 2026
Merged

Harden get_identity_apikey_only RPC permissions#1765
riderx merged 19 commits into
mainfrom
riderx/fix-attached-issue-2

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Mar 9, 2026

Summary

  • Revoke PUBLIC/anon/authenticated execute privilege from public.get_identity_apikey_only(key_mode[]) via migration so the RPC can no longer act as an unauthenticated API-key validity oracle.
  • Keep postgres and service_role execution on the same helper for internal backend callers.
  • Add regression coverage in tests/get-identity-apikey-only-rpc.test.ts to assert anon access is denied, service-role access still works, and invalid keys resolve to null.

Test plan

  • Run bun lint and bun lint:backend.
  • Run backend tests for tests/get-identity-apikey-only-rpc.test.ts in an environment with seeded data and required Supabase env vars.

Screenshots

  • Not applicable (backend/migration + test changes).

Checklist

  • My code follows the code style of this project and passes
    bun run lint:backend && bun run lint.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • My change has adequate E2E test coverage.
  • I have tested my code manually, and I have provided steps how to reproduce my tests

Summary by CodeRabbit

  • Bug Fixes

    • Hardened security: identity API key retrieval is no longer accessible to public, anonymous, or regular authenticated clients; only backend/service-role credentials can invoke it.
  • Tests

    • Added tests validating permission behavior for anonymous, authenticated, and service-role clients (including invalid service keys) and added test helpers for client setup and URL normalization.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 9, 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 tests for RPC permissions of get_identity_apikey_only and a DB migration that revokes public/anon/authenticated EXECUTE, granting it only to service_role and postgres.

Changes

Cohort / File(s) Summary
RPC Access Control Tests
tests/get-identity-apikey-only-rpc.test.ts
New test suite covering four scenarios for get_identity_apikey_only: anonymous denied, service_role allowed, authenticated denied, and service_role with invalid API key returns null. Adds helpers for localhost normalization and creating authenticated clients; uses env keys.
Permission Hardening Migration
supabase/migrations/20260312202155_hardening_get_identity_apikey_only_rpcs.sql
Alters privileges on public.get_identity_apikey_only(keymode public.key_mode[]): revokes ALL from PUBLIC, anon, authenticated; grants EXECUTE to service_role and postgres. Includes header comments documenting intent.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped where keys once danced in light,
Now gates are tightened through the night.
Tests knock softly, ears alert and keen,
Service roles watch the meadow green.
A carrot toast — secure and clean! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: hardening permissions on the get_identity_apikey_only RPC function by revoking public/anon/authenticated access.
Description check ✅ Passed The pull request description covers the required template sections with substantive content: Summary explains the security hardening, Test plan provides concrete steps, Screenshots section is appropriately marked N/A, and Checklist is mostly complete with linting confirmed.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch riderx/fix-attached-issue-2
📝 Coding Plan
  • Generate coding plan for human review comments

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

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

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

import { describe, expect, it } from 'vitest'
import { APIKEY_TEST_ALL } from './test-utils'

const SUPABASE_URL = env.SUPABASE_URL as string
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize SUPABASE_URL before creating clients

This test uses env.SUPABASE_URL directly, but the suite already documents and works around a real CI issue where localhost is resolved to ::1 while Supabase is bound to 127.0.0.1 (see the normalization helpers in tests/test-utils.ts). In environments where SUPABASE_URL is http://localhost:..., these RPC calls can fail with connection errors unrelated to permissions, making this new regression test flaky or consistently failing.

Useful? React with 👍 / 👎.

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

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

Inline comments:
In `@tests/get-identity-apikey-only-rpc.test.ts`:
- Around line 2-4: Reorder the top imports so built-in modules come first: move
"import { env } from 'node:process'" before the external import "import {
createClient } from '@supabase/supabase-js'"; leave the test imports ("import {
describe, expect, it } from 'vitest'") after the external import so the
file-level import order satisfies the ESLint builtin→external→test pattern.
- Around line 13-47: Add a new concurrent test that asserts the authenticated
role is denied RPC access: create a Supabase client that represents a logged-in
user (use createClient and set an authenticated JWT/session so the client acts
as the authenticated role), include the same global header { capgkey:
APIKEY_TEST_ALL }, call rpc('get_identity_apikey_only', { keymode: keyModes })
and assert data is null and error is not null with error.message containing
'permission denied' (mirroring the anon test structure but using an
authenticated session instead of SUPABASE_ANON_KEY or SUPABASE_SERVICE_KEY).
- Line 11: The test fails because keyModes is declared as a readonly tuple with
"as const" and cannot be passed into the RPC parameter typed as
Database["public"]["Enums"]["key_mode"][]; remove the "as const" so keyModes is
a mutable array whose element type matches
Database["public"]["Enums"]["key_mode"], or explicitly type it as
Database["public"]["Enums"]["key_mode"][] (update the const declaration of
keyModes used by the RPC calls in get-identity-apikey-only-rpc.test.ts).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c36e1c99-4967-4a6c-98b3-b162b0b924a3

📥 Commits

Reviewing files that changed from the base of the PR and between 2af3a08 and a8740dd.

📒 Files selected for processing (2)
  • supabase/migrations/20260308120000_hardening_get_identity_apikey_only_rpcs.sql
  • tests/get-identity-apikey-only-rpc.test.ts

Comment thread tests/get-identity-apikey-only-rpc.test.ts Outdated
Comment thread tests/get-identity-apikey-only-rpc.test.ts Outdated
Comment thread tests/get-identity-apikey-only-rpc.test.ts
Comment thread tests/get-identity-apikey-only-rpc.test.ts Fixed
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.

🧹 Nitpick comments (1)
tests/get-identity-apikey-only-rpc.test.ts (1)

7-19: Simplify redundant return logic.

When !raw evaluates to true, raw || '' will always return '' since raw is falsy. The conditional can be simplified.

♻️ Proposed simplification
 function normalizeLocalhostUrl(raw: string | undefined): string {
   if (!raw)
-    return raw || ''
+    return ''
   try {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/get-identity-apikey-only-rpc.test.ts` around lines 7 - 19, The guard in
normalizeLocalhostUrl uses the redundant expression "if (!raw) return raw ||
''"; change it to a simple explicit return for the falsy case (e.g., "if (!raw)
return ''") so the logic is clearer and no unnecessary fallback is evaluated;
keep the rest of the function (URL parsing, hostname substitution, and catch
behavior) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/get-identity-apikey-only-rpc.test.ts`:
- Around line 7-19: The guard in normalizeLocalhostUrl uses the redundant
expression "if (!raw) return raw || ''"; change it to a simple explicit return
for the falsy case (e.g., "if (!raw) return ''") so the logic is clearer and no
unnecessary fallback is evaluated; keep the rest of the function (URL parsing,
hostname substitution, and catch behavior) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c4cce2c-94d5-457a-828b-aa7d649f7293

📥 Commits

Reviewing files that changed from the base of the PR and between 680d4dd and 024d29f.

📒 Files selected for processing (1)
  • tests/get-identity-apikey-only-rpc.test.ts

@riderx riderx force-pushed the riderx/fix-attached-issue-2 branch from 5cabc70 to 024d29f Compare March 15, 2026 15:16
Comment thread tests/get-identity-apikey-only-rpc.test.ts Fixed
@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx merged commit 3f7d610 into main Mar 16, 2026
15 checks passed
@riderx riderx deleted the riderx/fix-attached-issue-2 branch March 16, 2026 14:05
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