supabase / supabase-js

An isomorphic Javascript client for Supabase. Query your Supabase database, subscribe to realtime events, upload and download files, browse typescript examples, invoke postgres functions via rpc, invoke supabase edge functions, query pgvector.
https://supabase.com
MIT License
2.83k stars 219 forks source link

Extremely dangerous bug: Partial .rpc(...) execution when client is called from Next14 Server Actions. Breaks ACID guarantee. #1002

Open sudowoodo200 opened 1 month ago

sudowoodo200 commented 1 month ago

Bug report

Describe the bug

A RPC call that only partially runs when called from a server action.

To Reproduce

I have a form that takes in two values: id and value, then calls the server action update defined below.

Server action file

"use server":
import {createClient} from @supabase/supabase-js

const admin = createClient(supabaseUrl, supabaseServiceRoleKey);
export async function update(id: string, value: number) {
   const { data, error } = await admin.rpc("update", {
      id: id,
      value: value,
  });
  return data
}

Table and Stored Procedure

create table users (
   id uuid references auth.users not null primary key,
   balance numeric default 0 not null
);
alter table users enable row level security;
create policy "Can view own user data." on users for select using (auth.uid() = id);

create or replace function public.update(id uuid, value numeric) 
returns numeric as $$
declare
  valid_value numeric;
begin
  select least(value, balance) into valid_value
  from users
  where id = id;

  update users
  set balance = balance - valid_value
  where id = id;

  return valid_value;
end;
$$ language plpgsql security definer;

Think of this as a deposit withdrawal. What happens?

This is not an RLS failure. I turned off RLS entirely for the users table and problem persists. The problem disappears when the Server Action block is moved to an API route and called from an API invocation.

Expected behavior

RPC runs entirely or not at all. In this case, RPC should always run, regardless of RLS, given the service_role key.

System information

Additional context

Add any other context about the problem here.