# Views in exposed schemas run with the caller permissions (security_invoker)

- **Pattern:** `ab-002643` (`supabase-rls-keys.rls-bypass-surfaces.views-use-security-invoker`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-10-03
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002643
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002643)

## Why it matters

A Postgres view runs with the privileges of the role that owns it, which on Supabase is `postgres`, a role that bypasses row-level security. So a view over an RLS-protected table, readable through the API, returns every row of the underlying table to anyone who can query the view: the table's policies are never consulted. The view looks like a harmless convenience ('a leaderboard', 'a dashboard summary'), and the RLS on the base table gives a false sense of safety. PostgreSQL 15 added `security_invoker`, which makes the view evaluate with the caller's privileges and therefore enforce the underlying RLS. Supabase's advisor flags the unsafe default as lint 0010, security definer view. AI tools generate `CREATE VIEW` freely and almost never add the option, so this is a common quiet bypass of an otherwise correct RLS setup.

## Severity rationale

High because a definer-style view silently defeats RLS on its base tables for every API caller, but it only exposes data reachable through views that exist, which is narrower than a missing-RLS table.

## Remediation

Create (or alter) every API-exposed view with `security_invoker = true` so the caller's RLS applies:

```sql
create or replace view public.my_open_tasks
  with (security_invoker = true) as
  select id, title, user_id from public.tasks where done = false;

-- or fix an existing view
alter view public.my_open_tasks set (security_invoker = true);
```

If a view must read data the caller cannot, put it in a non-exposed schema instead and expose a narrow, `security_invoker` view or a checked function. For a view over deliberately public data, add the `-- INTENTIONALLY_PUBLIC:` waiver line directly above `CREATE VIEW` with the reason.

## Detection

**The detection method for this pattern is part of AuditBuffet Pro.**

The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the [Supabase RLS & Keys audit](https://auditbuffet.com/audits/supabase-rls-keys). The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.

## External references

- cwe CWE-284
- cwe CWE-285
- owasp A01
- external 0010_security_definer_view — https://supabase.com/docs/guides/database/database-advisors?lint=0010_security_definer_view

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-002643
