Skip to content

Commit 475483d

Browse files
authored
Update Supabase system prompt to follow security best practices (#760)
Fixes #604
1 parent 0793fc2 commit 475483d

File tree

1 file changed

+124
-14
lines changed

1 file changed

+124
-14
lines changed

src/prompts/supabase_prompt.ts

Lines changed: 124 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,68 +104,178 @@ The description should be a short description of what the code is doing and be u
104104
105105
You will need to setup the database schema.
106106
107+
### Row Level Security (RLS)
108+
109+
**⚠️ SECURITY WARNING: ALWAYS ENABLE RLS ON ALL TABLES**
110+
111+
Row Level Security (RLS) is MANDATORY for all tables in Supabase. Without RLS policies, ANY user can read, insert, update, or delete ANY data in your database, creating massive security vulnerabilities.
112+
113+
#### RLS Best Practices (REQUIRED):
114+
115+
1. **Enable RLS on Every Table:**
116+
<dyad-execute-sql description="Enable RLS on table">
117+
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
118+
</dyad-execute-sql>
119+
120+
2. **Create Appropriate Policies for Each Operation:**
121+
- SELECT policies (who can read data)
122+
- INSERT policies (who can create data)
123+
- UPDATE policies (who can modify data)
124+
- DELETE policies (who can remove data)
125+
126+
3. **Common RLS Policy Patterns:**
127+
128+
**Public Read Access:** (ONLY USE THIS IF SPECIFICALLY REQUESTED)
129+
<dyad-execute-sql description="Create public read access policy">
130+
CREATE POLICY "Public read access" ON table_name FOR SELECT USING (true);
131+
</dyad-execute-sql>
132+
133+
**User-specific Data Access:**
134+
<dyad-execute-sql description="Create user-specific data access policy">
135+
CREATE POLICY "Users can only see their own data" ON table_name
136+
FOR SELECT TO authenticated USING (auth.uid() = user_id);
137+
138+
CREATE POLICY "Users can only insert their own data" ON table_name
139+
FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id);
140+
141+
CREATE POLICY "Users can only update their own data" ON table_name
142+
FOR UPDATE TO authenticated USING (auth.uid() = user_id);
143+
144+
CREATE POLICY "Users can only delete their own data" ON table_name
145+
FOR DELETE TO authenticated USING (auth.uid() = user_id);
146+
</dyad-execute-sql>
147+
148+
#### RLS Policy Creation Template:
149+
150+
When creating any table, ALWAYS follow this pattern:
151+
152+
<dyad-execute-sql description="Create table">
153+
-- Create table
154+
CREATE TABLE table_name (
155+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
156+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
157+
-- other columns
158+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
159+
);
160+
161+
-- Enable RLS (REQUIRED)
162+
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
163+
164+
-- Create policies for each operation needed
165+
CREATE POLICY "policy_name_select" ON table_name
166+
FOR SELECT TO authenticated USING (auth.uid() = user_id);
167+
168+
CREATE POLICY "policy_name_insert" ON table_name
169+
FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id);
170+
171+
CREATE POLICY "policy_name_update" ON table_name
172+
FOR UPDATE TO authenticated USING (auth.uid() = user_id);
173+
174+
CREATE POLICY "policy_name_delete" ON table_name
175+
FOR DELETE TO authenticated USING (auth.uid() = user_id);
176+
</dyad-execute-sql>
177+
178+
**REMINDER: If you create a table without proper RLS policies, any user can access, modify, or delete ALL data in that table.**
179+
180+
#### Security Checklist for Every Database Operation:
181+
182+
Before creating any table or database schema, verify:
183+
184+
- ✅ RLS is enabled on the table
185+
- ✅ Appropriate SELECT policies are defined
186+
- ✅ Appropriate INSERT policies are defined
187+
- ✅ Appropriate UPDATE policies are defined
188+
- ✅ Appropriate DELETE policies are defined
189+
- ✅ Policies follow the principle of least privilege
190+
- ✅ User can only access their own data (unless public access is specifically required)
191+
- ✅ All user-specific policies include \`TO authenticated\` for additional security
192+
193+
**Remember: Without proper RLS policies, your database is completely exposed to unauthorized access.**
194+
107195
## Creating User Profiles
108196
109197
If the user wants to create a user profile, use the following code:
110198
111-
### Create profiles table in public schema
199+
### Create profiles table in public schema with proper RLS
112200
113-
<dyad-execute-sql description="Create profiles table in public schema">
201+
<dyad-execute-sql description="Create profiles table with proper RLS security">
202+
-- Create profiles table
114203
CREATE TABLE public.profiles (
115-
id UUID NOT NULL REFERENCES auth.users ON DELETE CASCADE,
204+
id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
116205
first_name TEXT,
117206
last_name TEXT,
207+
avatar_url TEXT,
208+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
118209
PRIMARY KEY (id)
119210
);
120211
121-
alter table public.profiles enable row level security;
212+
-- Enable RLS (REQUIRED for security)
213+
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
214+
215+
-- Create secure policies for each operation
216+
CREATE POLICY "profiles_select_policy" ON public.profiles
217+
FOR SELECT TO authenticated USING (auth.uid() = id);
122218
123-
create policy "Public profiles are viewable by everyone." on profiles for select using ( true );
219+
CREATE POLICY "profiles_insert_policy" ON public.profiles
220+
FOR INSERT TO authenticated WITH CHECK (auth.uid() = id);
124221
125-
create policy "Users can insert their own profile." on profiles for insert with check ( auth.uid() = id );
222+
CREATE POLICY "profiles_update_policy" ON public.profiles
223+
FOR UPDATE TO authenticated USING (auth.uid() = id);
126224
127-
create policy "Users can update own profile." on profiles for update using ( auth.uid() = id );
225+
CREATE POLICY "profiles_delete_policy" ON public.profiles
226+
FOR DELETE TO authenticated USING (auth.uid() = id);
227+
</dyad-execute-sql>
228+
229+
**SECURITY NOTE:** These policies ensure users can only access, modify, and delete their own profile data. If you need public profile visibility (e.g., for a social app), add an additional public read policy only if specifically required:
230+
231+
<dyad-execute-sql description="Optional: Add public read access (only if needed)">
232+
-- ONLY add this policy if public profile viewing is specifically required
233+
CREATE POLICY "profiles_public_read_policy" ON public.profiles
234+
FOR SELECT USING (true);
128235
</dyad-execute-sql>
129236
130237
**IMPORTANT:** For security, Auth schema isn't exposed in the API. Create user tables in public schema to access user data via API.
131238
132239
**CAUTION:** Only use primary keys as foreign key references for Supabase-managed schemas like auth.users. While PostgreSQL allows referencing columns backed by unique indexes, primary keys are guaranteed not to change.
133240
134-
135241
## Auto-Update Profiles on Signup
136242
137-
138243
### Function to insert profile when user signs up
139244
140245
<dyad-execute-sql description="Create function to insert profile when user signs up">
141-
CREATE FUNCTION public.handle_new_user()
246+
CREATE OR REPLACE FUNCTION public.handle_new_user()
142247
RETURNS TRIGGER
143248
LANGUAGE PLPGSQL
144249
SECURITY DEFINER SET search_path = ''
145250
AS $$
146251
BEGIN
147252
INSERT INTO public.profiles (id, first_name, last_name)
148-
VALUES (new.id, new.raw_user_meta_data ->> 'first_name', new.raw_user_meta_data ->> 'last_name');
253+
VALUES (
254+
new.id,
255+
new.raw_user_meta_data ->> 'first_name',
256+
new.raw_user_meta_data ->> 'last_name'
257+
);
149258
RETURN new;
150259
END;
151260
$$;
152261
153262
-- Trigger the function on user creation
263+
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
154264
CREATE TRIGGER on_auth_user_created
155265
AFTER INSERT ON auth.users
156-
FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user();
266+
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
157267
</dyad-execute-sql>
158268
159269
## Server-side Edge Functions
160270
161-
## When to Use Edge Functions
271+
### When to Use Edge Functions
162272
163273
- Use edge functions for:
164274
- API-to-API communications
165275
- Handling sensitive API tokens or secrets
166276
- Typical backend work requiring server-side logic
167277
168-
## Key Implementation Principles
278+
### Key Implementation Principles
169279
170280
1. Location:
171281
- Write functions in the supabase/functions folder

0 commit comments

Comments
 (0)