Skip to content

Harden rescind_invitation RPC auth#1757

Open
riderx wants to merge 4 commits intomainfrom
riderx/fix-org-oracle
Open

Harden rescind_invitation RPC auth#1757
riderx wants to merge 4 commits intomainfrom
riderx/fix-org-oracle

Conversation

@riderx
Copy link
Member

@riderx riderx commented Mar 8, 2026

Summary (AI generated)

  • Added a migration to harden public.rescind_invitation by denying unauthenticated execution, keeping sensitive behavior secure, and preserving existing authorized status outcomes.
  • Added regression tests to ensure non-admin callers receive the same NO_RIGHTS response across missing/invalid org IDs and that anon cannot execute the RPC.

Test plan (AI generated)

Screenshots (AI generated)

  • Not applicable (backend SQL and migration changes only).

Checklist (AI generated)

  • 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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 8, 2026

Warning

Rate limit exceeded

@riderx has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a9b0ffb0-f1a0-43ce-a873-b1b77900b7f7

📥 Commits

Reviewing files that changed from the base of the PR and between 5a9c37f and 5e00d59.

📒 Files selected for processing (2)
  • supabase/migrations/20260308203222_fix_rescind_invitation_rpc_access_hardening.sql
  • supabase/tests/28_test_new_migration_functions.sql
📝 Walkthrough

Walkthrough

Adds a new security-hardened PostgreSQL RPC function public.rescind_invitation(email TEXT, org_id UUID) that enforces check_min_rights, validates organization and invitation presence in tmp_users, updates cancelled_at when applicable, returns status strings, and adjusts function EXECUTE permissions for anon/authenticated/service_role.

Changes

Cohort / File(s) Summary
Migration - rescind_invitation function
supabase/migrations/20260308203222_fix_rescind_invitation_rpc_access_hardening.sql
Adds public.rescind_invitation(email, org_id) implementing rights check via public.check_min_rights, org existence check, invitation lookup in tmp_users, status returns (NO_RIGHTS, NO_INVITATION, ALREADY_CANCELLED, OK), cancelled_at update, and REVOKE/GRANT permission adjustments for public/anon/authenticated/service_role.
Tests - rescind_invitation behavior & privileges
supabase/tests/28_test_new_migration_functions.sql
Adds assertions verifying NO_RIGHTS consistency for non-admins vs nonexistent orgs, checks anon lacks EXECUTE on the new function, and repeats NO_RIGHTS checks under anon context.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Caller
    participant RPC as rescind_invitation()
    participant Rights as public.check_min_rights()
    participant Orgs as organizations table
    participant Tmp as tmp_users table

    Caller->>RPC: CALL rescind_invitation(email, org_id)
    RPC->>Rights: VERIFY caller rights for org_id
    Rights-->>RPC: [allowed|denied]
    alt denied
      RPC-->>Caller: "NO_RIGHTS"
    else allowed
      RPC->>Orgs: CHECK org exists (org_id)
      Orgs-->>RPC: [exists|not found]
      alt not found
        RPC-->>Caller: "NO_INVITATION"
      else exists
        RPC->>Tmp: SELECT invitation by email+org_id
        Tmp-->>RPC: [row|no row]
        alt no row
          RPC-->>Caller: "NO_INVITATION"
        else row
          alt cancelled_at IS NOT NULL
            RPC-->>Caller: "ALREADY_CANCELLED"
          else
            RPC->>Tmp: UPDATE cancelled_at = now()
            Tmp-->>RPC: updated
            RPC-->>Caller: "OK"
          end
        end
      end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through rows and roles tonight,

closed an invite with a polite bite,
Rights checked, timestamps set true,
No stray access — that's what I do,
🥕 Securely done, a rabbit's delight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Harden rescind_invitation RPC auth' clearly and specifically summarizes the main change: security hardening of the rescind_invitation RPC function's authentication.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description covers all required template sections with sufficient detail about the migration, test plan, and testing approach performed.

✏️ 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-org-oracle

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

🤖 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/20260308203222_fix_rescind_invitation_rpc_access_hardening.sql`:
- Around line 37-52: The SELECT that reads the temporary invitation into
tmp_user is racy; modify the SELECT ... INTO tmp_user query (the one that
selects from public.tmp_users using rescind_invitation.email and
rescind_invitation.org_id) to append FOR UPDATE so the row is locked for the
duration of the transaction, ensuring subsequent logic (the IF
tmp_user.cancelled_at check and the UPDATE of public.tmp_users) sees the
serialized state and the second concurrent call returns ALREADY_CANCELLED.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b842f3f9-55ae-4591-9dce-7213c8c628d0

📥 Commits

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

📒 Files selected for processing (1)
  • supabase/migrations/20260308203222_fix_rescind_invitation_rpc_access_hardening.sql

@riderx riderx force-pushed the riderx/fix-org-oracle branch from 5a9c37f to 936266a Compare March 8, 2026 23:52
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

🤖 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/tests/28_test_new_migration_functions.sql`:
- Around line 240-251: The pgTAP plan declaration still reads plan(28) but you
added two assertions (rescind_invitation tests), so update the plan count to
plan(30) where the file declares the test plan (the initial plan(...) near the
top of the SQL file and the other matching plan declaration referenced in the
similar block around lines where the second set of rescind_invitation assertions
appear) so the planned test count matches the actual assertions; search for the
literal "plan(28)" and change it to "plan(30)" to fix the planned-vs-ran
mismatch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b91a1b2a-5def-400a-b0da-0da453dbeedc

📥 Commits

Reviewing files that changed from the base of the PR and between 8054a2d and 5a9c37f.

📒 Files selected for processing (1)
  • supabase/tests/28_test_new_migration_functions.sql

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 9, 2026

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