You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* add schema-constraints reference for safe migration patterns ([#30](https://github.com/supabase/agent-skills/issues/30)) ([9b236f3](https://github.com/supabase/agent-skills/commit/9b236f3ebd65d76a2c570f19931353da9c858d5a))
8
+
* using Supabase agent skills ([#12](https://github.com/supabase/agent-skills/issues/12)) ([7c2e389](https://github.com/supabase/agent-skills/commit/7c2e3894fddfde8eb6c77d2a8921904543b9be7a))
9
+
10
+
### Bug Fixes
11
+
12
+
* correct broken reference link in postgres best practices skill ([#58](https://github.com/supabase/agent-skills/issues/58)) ([f4e2277](https://github.com/supabase/agent-skills/commit/f4e22777fd8573537297b568c16e5a45a25927da))
13
+
* cover SECURITY DEFINER, auth.role() deprecation, and BOLA in security checklist ([#85](https://github.com/supabase/agent-skills/issues/85)) ([133f43e](https://github.com/supabase/agent-skills/commit/133f43e8c2ffc48823ff0630c692cabecea3e3a3))
Copy file name to clipboardExpand all lines: plugins/supabase/skills/supabase-postgres-best-practices/references/security-rls-performance.md
+9-3Lines changed: 9 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,23 +29,29 @@ create policy orders_policy on orders
29
29
30
30
Use security definer functions for complex checks:
31
31
32
+
`SECURITY DEFINER` functions run with the creator's privileges and bypass RLS on any tables they touch — which is what makes them useful for internal lookups, but also what makes them dangerous if misused. Always include an explicit `auth.uid()` check inside the function body, keep them in a non-exposed schema, and revoke `EXECUTE` from any role that shouldn't call them directly.
33
+
32
34
```sql
33
-
-- Create helper function (runs as definer, bypasses RLS)
34
-
create or replacefunctionis_team_member(team_id bigint)
35
+
-- Create helper function in a private schema
36
+
create or replacefunctionprivate.is_team_member(team_id bigint)
35
37
returns boolean
36
38
language sql
37
39
security definer
38
40
set search_path =''
39
41
as $$
40
42
select exists (
41
43
select1frompublic.team_members
44
+
-- always check the calling user's identity inside the function
42
45
where team_id = $1and user_id = (selectauth.uid())
43
46
);
44
47
$$;
45
48
49
+
-- Revoke direct execution from public roles
50
+
revoke execute on function private.is_team_member(bigint) from PUBLIC, anon, authenticated, service_role;
51
+
46
52
-- Use in policy (indexed lookup, not per-row check)
47
53
create policy team_orders_policy on orders
48
-
using ((select is_team_member(team_id)));
54
+
using ((selectprivate.is_team_member(team_id)));
49
55
```
50
56
51
57
Always add indexes on columns used in RLS policies:
* instructions on exposing tables to the data api ([#71](https://github.com/supabase/agent-skills/issues/71)) ([f15a5a4](https://github.com/supabase/agent-skills/commit/f15a5a40779072a530c9e53c3f14ec4131118ea6))
10
+
* using Supabase agent skills ([#12](https://github.com/supabase/agent-skills/issues/12)) ([7c2e389](https://github.com/supabase/agent-skills/commit/7c2e3894fddfde8eb6c77d2a8921904543b9be7a))
11
+
12
+
### Bug Fixes
13
+
14
+
* bump supabase skill to v0.1.1 and fix Data API broken link ([#72](https://github.com/supabase/agent-skills/issues/72)) ([5a6542e](https://github.com/supabase/agent-skills/commit/5a6542e08fc026d90c9a6a0f5a67749e9ceb9946))
15
+
* cover SECURITY DEFINER, auth.role() deprecation, and BOLA in security checklist ([#85](https://github.com/supabase/agent-skills/issues/85)) ([133f43e](https://github.com/supabase/agent-skills/commit/133f43e8c2ffc48823ff0630c692cabecea3e3a3))
16
+
* update Data API doc link and bump supabase skill to v0.1.1 ([#73](https://github.com/supabase/agent-skills/issues/73)) ([e5f7a7c](https://github.com/supabase/agent-skills/commit/e5f7a7cfd697765848ffd6a4505f3c02e1ee17ee))
Copy file name to clipboardExpand all lines: plugins/supabase/skills/supabase/SKILL.md
+24-1Lines changed: 24 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -44,11 +44,34 @@ When working on any Supabase task that touches auth, RLS, views, storage, or use
44
44
-**RLS, views, and privileged database code**
45
45
-**Views bypass RLS by default.** In Postgres 15 and above, use `CREATE VIEW ... WITH (security_invoker = true)`. In older versions of Postgres, protect your views by revoking access from the `anon` and `authenticated` roles, or by putting them in an unexposed schema.
46
46
-**UPDATE requires a SELECT policy.** In Postgres RLS, an UPDATE needs to first SELECT the row. Without a SELECT policy, updates silently return 0 rows — no error, just no change.
47
-
-**Do not put `security definer` functions in an exposed schema.** Keep them in a private or otherwise unexposed schema.
47
+
-**`auth.role()` is deprecated — use the `TO` clause instead.** Supabase has deprecated `auth.role()` in favour of specifying the target role directly on the policy with `TO authenticated` or `TO anon`. Beyond deprecation, `auth.role() = 'authenticated'` breaks silently when anonymous sign-ins are enabled, because anonymous users carry the `authenticated` Postgres role and pass the check regardless of whether the user is genuinely signed in.
48
+
```sql
49
+
-- Deprecated (do not use)
50
+
create policy "example"on table_name for select
51
+
using ( auth.role() ='authenticated' );
52
+
```
53
+
-**`TO authenticated` alone is authentication without authorization (BOLA / IDOR).** Using `TO authenticated` only checks the role — it does not restrict which rows a user can access. The correct pattern combines `TO authenticated` with an ownership predicate in`USING`:
54
+
```sql
55
+
create policy "example" on table_name for select
56
+
to authenticated
57
+
using ( (select auth.uid()) = user_id );
58
+
```
59
+
-**UPDATE policies require both `USING`and`WITH CHECK`.** Without `WITH CHECK`, a user can reassign a row's `user_id` to another user:
60
+
```sql
61
+
create policy "example" on table_name for update
62
+
to authenticated
63
+
using ( (select auth.uid()) = user_id )
64
+
with check ( (select auth.uid()) = user_id );
65
+
```
66
+
- **`SECURITY DEFINER` functions bypass RLS.** A `SECURITY DEFINER` function runs with its creator's privileges — typically a role with `bypassrls` (e.g., `postgres`). Never add `SECURITY DEFINER` to resolve a permission error; it silently removes access control without fixing the underlying cause. Prefer `SECURITY INVOKER`.
67
+
-**`SECURITY DEFINER` functions in`public` are callable by all roles.** Postgres grants `EXECUTE` to `PUBLIC` by default for every new function, so any `SECURITY DEFINER` function in`public` is a public API endpoint callable by `anon`and`authenticated` (which inherit from`PUBLIC`) without any additional grant. When `SECURITY DEFINER` is genuinely needed (e.g., bypassing RLS on an internal lookup table), keep the function in a non-exposed schema, always include an `auth.uid()`checkin the function body, and run `supabase db advisors` after making changes.
48
68
49
69
-**Storage access control**
50
70
-**Storage upsert requires INSERT +SELECT+UPDATE.** Granting only INSERT allows new uploads but file replacement (upsert) silently fails. You need all three.
51
71
72
+
-**Dependency and supply-chain security**
73
+
-**Always pin package versions andcommit lockfiles** when installing Supabase packages (`supabase-js`, `@supabase/ssr`, `supabase-py`, etc.). See the [npm security guide](https://supabase.com/docs/guides/security/npm-security.md) for the full checklist.
74
+
52
75
For any security concern not covered above, fetch the Supabase product security index: `https://supabase.com/docs/guides/security/product-security.md`
0 commit comments