Skip to content

feat(studio): warn before CREATE TABLE without RLS in SQL editor#45008

Merged
alaister merged 5 commits into
masterfrom
feat/sql-editor-rls-warning
Apr 18, 2026
Merged

feat(studio): warn before CREATE TABLE without RLS in SQL editor#45008
alaister merged 5 commits into
masterfrom
feat/sql-editor-rls-warning

Conversation

@alaister

@alaister alaister commented Apr 18, 2026

Copy link
Copy Markdown
Member

Adds a pre-execution warning in the SQL editor when a CREATE TABLE statement is run without enabling Row Level Security on the new table. Responds to the press call-out around SQL editor security.

Screenshot 2026-04-18 at 4 31 07 PM

Added:

  • Pre-execution check in executeQuery that detects CREATE TABLE statements without a matching ALTER TABLE ... ENABLE ROW LEVEL SECURITY in the same submitted SQL.
  • New "Run and enable RLS" action in the warning modal that rewrites the SQL to append ALTER TABLE [schema.]<table> ENABLE ROW LEVEL SECURITY; for each detected table before running.
  • Link in the modal to the RLS docs.

Changed:

  • RunQueryWarningModal now renders Dialog directly (instead of ConfirmationModal) so it can show three buttons: Cancel / Run without RLS / Run and enable RLS.
  • sqlEventParser table-name regex now supports quoted identifiers containing spaces (e.g. "My Table") and escaped quotes (e.g. "user""table").

The check runs against the SQL that's actually submitted, so partial-selection works correctly — selecting only the CREATE TABLE portion will trigger the warning even if there's a matching ENABLE RLS lower in the editor.

To test

  • Open the SQL editor and run create table foo (id int8 primary key); → modal should appear with the RLS warning bullet and three buttons.
  • Click Run and enable RLS → query runs, table is created with RLS enabled.
  • Click Run without RLS → query runs as written, no RLS.
  • Run create table foo (id int8); alter table foo enable row level security; → no modal (RLS already enabled in same submission).
  • Run create table public.bar (id int8); create table baz (id int8); alter table baz enable rls; → modal flags only public.bar.
  • Select only the create table portion of a snippet that also enables RLS lower down and run the selection → modal should still fire.
  • Run an existing destructive query (drop table x) → modal still works as before with two buttons (Cancel / Run this query).

Summary by CodeRabbit

  • New Features

    • SQL editor now detects CREATE TABLE statements missing Row Level Security (RLS) and shows counts and dynamic table/schema details in a redesigned warning dialog with updated pluralization and a “Learn more” link.
    • New actions: “Run without RLS” and, when available, “Run and enable RLS” which applies RLS and runs the query; editor can execute an overridden SQL payload when applying RLS changes.
  • Tests

    • Added comprehensive unit and e2e tests covering RLS detection, SQL augmentation, trigger handling, identifier parsing, and the “Run and enable RLS” flow.

Detects CREATE TABLE statements in the SQL editor that don't have a
matching ALTER TABLE ... ENABLE ROW LEVEL SECURITY in the same submission
and prompts the user before running. Also extends sqlEventParser to
support quoted identifiers containing spaces.
@alaister alaister requested a review from a team as a code owner April 18, 2026 08:28
@vercel

vercel Bot commented Apr 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

8 Skipped Deployments
Project Deployment Actions Updated (UTC)
studio Ignored Ignored Apr 18, 2026 9:00am
design-system Skipped Skipped Apr 18, 2026 9:00am
docs Skipped Skipped Apr 18, 2026 9:00am
learn Skipped Skipped Apr 18, 2026 9:00am
studio-self-hosted Skipped Skipped Apr 18, 2026 9:00am
studio-staging Skipped Skipped Apr 18, 2026 9:00am
ui-library Skipped Skipped Apr 18, 2026 9:00am
zone-www-dot-com Skipped Skipped Apr 18, 2026 9:00am

Request Review

@supabase

supabase Bot commented Apr 18, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project xguihxuzqibwxjnimxev because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b7f790e1-7f02-4157-bda4-f29c57b1a784

📥 Commits

Reviewing files that changed from the base of the PR and between 253d6d1 and 9a85036.

📒 Files selected for processing (1)
  • e2e/studio/features/sql-editor.spec.ts

📝 Walkthrough

Walkthrough

Detects CREATE TABLE statements missing RLS, shows a warning dialog listing affected tables, and offers running the query as-is or rewriting the SQL to append ALTER TABLE ... ENABLE ROW LEVEL SECURITY before executing. (Includes editor integration, parser/util updates, modal UI changes, tests, and an e2e test.)

Changes

Cohort / File(s) Summary
RLS detection & SQL utilities
apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts, apps/studio/lib/sql-event-parser.ts
Added types and helpers to detect CREATE TABLE targets missing RLS, filter tables covered by an "ensure RLS" trigger, append ALTER TABLE ... ENABLE ROW LEVEL SECURITY statements, and broadened identifier regexes in the SQL event parser.
Editor integration
apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx
Threaded database event triggers into execution flow, detect missing-RLS tables pre-execution (populate potentialIssues.createTablesMissingRLS), support sqlOverride execution, and handle onConfirmWithRLS to rewrite SQL, clear modal state, refocus editor, and execute with force=true.
Modal UI
apps/studio/components/interfaces/SQLEditor/RunQueryWarningModal.tsx
Replaced ConfirmationModal with a Dialog composition, added optional onConfirmWithRLS prop, updated title/labels and pluralization, added missing-RLS warning item with dynamic table/schema rendering and "Learn more" link, adjusted action buttons and close handling via onOpenChange.
Types
apps/studio/components/interfaces/SQLEditor/SQLEditor.types.ts
Extended exported PotentialIssues with optional createTablesMissingRLS?: { schema?: string; tableName: string }[].
Tests
apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts
Added comprehensive unit tests for CREATE TABLE detection (variants, quoting, comments), appendEnableRLSStatements, and trigger helpers (hasActiveEnsureRLSTrigger, filterTablesCoveredByEnsureRLSTrigger).
E2E
e2e/studio/features/sql-editor.spec.ts
Added Playwright test that verifies the RLS warning appears for CREATE TABLE without RLS and that "Run and enable RLS" creates the table with RLS enabled, including DB verification and cleanup.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Editor as SQL Editor
    participant Parser as RLS Utils / Parser
    participant Modal as Warning Dialog
    participant Executor as Query Executor

    User->>Editor: Submit query
    Editor->>Parser: getCreateTablesMissingRLS(sql)
    Parser-->>Editor: missing-RLS tables
    alt missing-RLS exists
        Editor->>Modal: open with potentialIssues
        Modal->>User: show missing tables & actions
        alt User clicks "Run and enable RLS"
            User->>Modal: confirm with RLS
            Modal->>Editor: onConfirmWithRLS()
            Editor->>Parser: appendEnableRLSStatements(sql, tables)
            Parser-->>Editor: rewrittenSQL
            Editor->>Executor: executeQuery(force=true, rewrittenSQL)
        else User clicks "Run without RLS"
            User->>Modal: confirm without RLS
            Modal->>Editor: onConfirm()
            Editor->>Executor: executeQuery(force=true, originalSQL)
        else User cancels
            User->>Modal: Close dialog
            Modal->>Editor: onCancel()
        end
    else no missing-RLS
        Editor->>Executor: executeQuery(force=false, originalSQL)
    end
    Executor-->>User: Return results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I sniffed the SQL, found tables bare,

Missing RLS — I paused with care.
I tack on ALTERs, tidy each row,
Then hop to the editor, ready to go.
🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main feature: adding a warning in the SQL editor when CREATE TABLE statements lack RLS, which is the core change across multiple files.
Description check ✅ Passed The description covers the contribution agreement, change type (feature), current/new behavior with visual context, and detailed testing steps. However, it does not explicitly follow the template structure by answering all required template fields (e.g., 'What is the current behavior?' and 'What kind of change does this PR introduce?' are embedded rather than clearly sectioned).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sql-editor-rls-warning

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts`:
- Around line 102-119: The quote() helper in appendEnableRLSStatements
incorrectly uses a case-insensitive regex, so mixed-case identifiers like
MyTable (originally quoted) pass as unquoted and are emitted without quotes;
remove the /i flag (use /^[a-z_][a-z0-9_]*$/) or otherwise ensure only
all-lowercase unquoted identifiers pass, and re-quote any identifier that is not
strictly lowercase-identifier-safe before building target; update
appendEnableRLSStatements (and its quote function) accordingly and add a unit
test for CREATE TABLE "MyTable" to cover the regression.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 67d46420-2c24-4592-b546-d38b94ed34de

📥 Commits

Reviewing files that changed from the base of the PR and between 0849fad and f96522e.

📒 Files selected for processing (6)
  • apps/studio/components/interfaces/SQLEditor/RunQueryWarningModal.tsx
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.types.ts
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts
  • apps/studio/lib/sql-event-parser.ts

Comment thread apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts
@github-actions

github-actions Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

🎭 Playwright Test Results

passed  200 passed
flaky  1 flaky
skipped  5 skipped

Details

stats  206 tests across 22 suites
duration  11 minutes, 58 seconds
commit  9a85036

Flaky tests

Features › realtime-inspector.spec.ts › Realtime Inspector › Broadcast Messages › broadcast modal validates JSON payload

Skipped tests

Features › auth-users.spec.ts › should show web3 users as enabled when the matching web3 provider is enabled
Features › sql-editor.spec.ts › SQL Editor › snippet favourite works as expected
Features › sql-editor.spec.ts › SQL Editor › share with team works as expected
Features › sql-editor.spec.ts › SQL Editor › folders works as expected
Features › sql-editor.spec.ts › SQL Editor › other SQL snippets actions work as expected

Reads the project's event triggers and suppresses the warning for
CREATE TABLE statements in the public schema when the ensure_rls
trigger (or any trigger using the rls_auto_enable function) is active.
Tables in other schemas still trigger the warning since the trigger
only enables RLS on public-schema tables.
The simple-identifier check in appendEnableRLSStatements used a
case-insensitive regex, so mixed-case names like "MyTable" (whose
quotes get stripped by sqlEventParser.cleanIdentifier) were emitted
unquoted. Postgres then folded them to lowercase and the appended
ALTER would target a table that doesn't exist. Drops the /i flag and
adds regression tests for "MyTable" and "MySchema"."MyTable".

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts (1)

133-134: ⚠️ Potential issue | 🔴 Critical

Fix quoting for mixed-case quoted identifiers before appending ALTER.

This unresolved issue still emits MyTable unquoted for an originally quoted "MyTable", causing PostgreSQL to target mytable instead. Remove the case-insensitive match, or normalize unquoted identifiers in the parser before this point so only lowercase-safe names are emitted unquoted.

🔧 Proposed fix
   const quote = (identifier: string) =>
-    /^[a-z_][a-z0-9_]*$/i.test(identifier) ? identifier : `"${identifier.replace(/"/g, '""')}"`
+    /^[a-z_][a-z0-9_]*$/.test(identifier) ? identifier : `"${identifier.replace(/"/g, '""')}"`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts` around lines
133 - 134, The quote helper incorrectly treats mixed-case identifiers like
"MyTable" as safe to emit unquoted because the regex uses the case-insensitive
flag; update the logic in the quote function so it only leaves identifiers
unquoted when they match the lowercase-safe SQL identifier pattern (remove the
/i flag) or ensure identifiers are normalized to lowercase before calling quote;
specifically modify the quote(identifier: string) implementation (and any call
sites that append ALTER) so that mixed-case or originally-quoted names are
always quoted via identifier.replace(/"/g,'""') wrapped in double-quotes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts`:
- Around line 143-146: The appended statement terminator can be swallowed when
the submitted SQL ends with a trailing line comment; update the logic that
builds the final string (using variables trimmed, separator, additions) so that
if trimmed does not already end with ';' but ends with a line comment (match
/--.*$/), you insert a standalone semicolon on its own line before adding the
comment block and additions; otherwise keep the existing behavior (use '\n\n' if
already ';' or ';\n\n' otherwise). This ensures the ';' is not inside a trailing
'--' comment and that subsequent ALTER/ENABLE RLS statements are parsed as
separate statements.
- Around line 114-123: The current logic lowercases schema/table keys when
building rlsEnabled and when checking for TableCreated events, which collapses
quoted PostgreSQL identifiers; update the keying to use a parser-normalized
identity that preserves quoted identifier case and only folds unquoted
identifiers (e.g., add a helper normalizeIdentifier(schema, tableName) and use
it when building the rlsEnabled Set and when checking .has and mapping results).
Locate the code that constructs rlsEnabled (filtering
TABLE_EVENT_ACTIONS.TableRLSEnabled) and the subsequent .filter/.map for
TABLE_EVENT_ACTIONS.TableCreated and replace the raw `${e.schema ??
''}.${e.tableName}`.toLowerCase() usage with calls to the new normalize function
so quoted names remain case-sensitive while unquoted names are folded.

---

Duplicate comments:
In `@apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts`:
- Around line 133-134: The quote helper incorrectly treats mixed-case
identifiers like "MyTable" as safe to emit unquoted because the regex uses the
case-insensitive flag; update the logic in the quote function so it only leaves
identifiers unquoted when they match the lowercase-safe SQL identifier pattern
(remove the /i flag) or ensure identifiers are normalized to lowercase before
calling quote; specifically modify the quote(identifier: string) implementation
(and any call sites that append ALTER) so that mixed-case or originally-quoted
names are always quoted via identifier.replace(/"/g,'""') wrapped in
double-quotes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: f90ba822-3d81-4aac-9cf9-c65bb57e5d19

📥 Commits

Reviewing files that changed from the base of the PR and between f96522e and 8646b83.

📒 Files selected for processing (3)
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts

Comment thread apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts
Comment thread apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.ts
Two fixes from PR review:

- getCreateTablesMissingRLS no longer lowercases match keys, so quoted
  identifiers like "MyTable" and "mytable" (distinct tables in Postgres)
  don't collide and silently suppress the warning.
- appendEnableRLSStatements puts the appended ';' on its own line when
  the SQL ends with a trailing line comment, so the terminator isn't
  swallowed and the following ALTER TABLE is parsed as a new statement.
Single Playwright test that types a CREATE TABLE statement, asserts the
warning modal appears with the RLS bullet, clicks "Run and enable RLS",
and verifies the table exists with relrowsecurity = true.
@alaister alaister merged commit 3aed9a9 into master Apr 18, 2026
35 of 37 checks passed
@alaister alaister deleted the feat/sql-editor-rls-warning branch April 18, 2026 09:18
@github-actions

github-actions Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Braintrust eval report

Assistant (master-1776503993)

Score Average Improvements Regressions
Completeness 100% (+2pp) 1 🟢 -
Conciseness 25.5% (-3pp) 3 🟢 4 🔴
Goal Completion 90.2% (-2pp) - 2 🔴
Knowledge Usage 100% (+0pp) - -
SQL Identifier Quoting 92.3% (-8pp) - 1 🔴
SQL Validity 92.3% (-8pp) - 1 🔴
Tool Usage 73.8% (+0pp) - -
Docs Faithfulness 66.7% (-12pp) 1 🟢 5 🔴
Correctness 95.8% (-4pp) - 1 🔴
URL Validity 100% (+0pp) - -
Time_to_first_token 0.01tok (0tok) 14 🟢 3 🔴
Llm_calls 12.35 (+0.06) 9 🟢 6 🔴
Tool_calls 3.14 (-0.18) 6 🟢 8 🔴
Errors 0 (+0) - -
Llm_errors 0 (+0) - -
Tool_errors 0 (+0) - -
Prompt_tokens 24329.9tok (+596.45tok) 9 🟢 8 🔴
Prompt_cached_tokens 0tok (+0tok) - -
Prompt_cache_creation_tokens 0tok (+0tok) - -
Completion_tokens 654.57tok (+66.43tok) 10 🟢 7 🔴
Completion_reasoning_tokens 0tok (+0tok) - -
Completion_accepted_prediction_tokens 0tok (+0tok) - -
Completion_rejected_prediction_tokens 0tok (+0tok) - -
Completion_audio_tokens 0tok (+0tok) - -
Total_tokens 24984.47tok (+662.88tok) 9 🟢 8 🔴
Estimated_cost 0.01$ (+0$) 8 🟢 7 🔴
Duration 31.34s (+12.08s) 10 🟢 7 🔴
Llm_duration 87.09s (+38.47s) 10 🟢 7 🔴

alaister added a commit that referenced this pull request Apr 20, 2026
…45050)

Fixes a false positive in the CREATE-TABLE-without-RLS warning modal
added in #45008. The warning was firing on `CREATE FUNCTION` statements
because the `SELECT..INTO` detector was matching plpgsql variable
assignments inside `$$…$$` function bodies.

Reported example that triggered the modal with no table actually being
created:

```sql
create or replace function schema_checks()
returns jsonb
language plpgsql
as $$
declare
  ret jsonb;
begin
  select jsonb_build_object('value', 'ok') into ret;
  return ret;
end;
$$;
```

**Changed:**
- `SQLEventParser.match()` now strips the body of `$tag$…$tag$` blocks
before running detectors. Tags are kept as markers; content is blanked
out so function bodies, DO blocks, and dollar-quoted string literals are
never scanned as DDL.
- Updated a pre-existing parser test that asserted the buggy behaviour
(it expected `CREATE TABLE fake` inside a `$$…$$` string literal to be
detected — `$$…$$` is a string literal in Postgres, not DDL).

**Added:**
- Regression tests in `SQLEditor.utils.test.ts` covering: the exact
reported function, DO blocks with `select into`, `create table` text
inside a function body, mixed top-level `CREATE TABLE` + function with
`INTO` assignments, and custom `$body$…$body$` tags.
- Parser-level regression test in `sql-event-parser.test.ts`.

## To test

- In the SQL editor, paste the function from the Slack report and run it
— the RLS warning modal should not appear.
- Run `create table foo (id int8 primary key);` on its own — modal still
appears as before.
- Run `create table foo (id int8); create or replace function bar()
returns int language plpgsql as $$ declare v int; begin select 1 into v;
return v; end; $$;` — modal should flag only `foo`, not `v`.
- Run an existing destructive query (`drop table x`) — unaffected, modal
still works.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Parser no longer treats DDL/DML-like text inside PL/pgSQL functions,
DO blocks, or dollar-quoted bodies (including nested/custom tags) as
top-level CREATE TABLE/SELECT INTO, preventing false detections and UI
warnings.

* **Tests**
* Added unit and e2e regression tests covering dollar-quoted blocks,
nested dollar tags, DO blocks, SELECT INTO inside functions, and
positive controls with a real top-level CREATE TABLE.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
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.

2 participants