From 3036bb103b3214bfbf7e87ffb8321f527b38af1a Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Mon, 4 May 2026 16:21:47 +0800 Subject: [PATCH] [FE-3134] fix(studio): handle ALTER TABLE IF EXISTS in RLS detection (#45493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SQL Editor was warning about missing RLS even when the same query enabled it, if the user wrote `ALTER TABLE IF EXISTS ...`. The parser regex didn't recognise `IF EXISTS` and was capturing `IF` as the table name, so the RLS event never matched the `CREATE TABLE`. **Changed:** - `ALTER TABLE` regex in `sql-event-parser.ts` now accepts the optional `IF EXISTS` and `ONLY` modifiers, matching Postgres's `ALTER TABLE [ IF EXISTS ] [ ONLY ] name` grammar. **Added:** - Unit tests for `IF EXISTS`, `ONLY`, and both combined. - Regression test in `SQLEditor.utils.test.ts` using the customer's exact SQL. ## To test 1. Open the SQL Editor and paste: ```sql CREATE TABLE IF NOT EXISTS public."Conversations" (id int8 primary key); ALTER TABLE IF EXISTS public."Conversations" ENABLE ROW LEVEL SECURITY; ``` 2. Hit Run – the "table will not have RLS" warning should **not** appear. 3. Sanity check: a `CREATE TABLE` without any matching `ENABLE ROW LEVEL SECURITY` still triggers the warning. Addresses [FE-3134](https://linear.app/supabase/issue/FE-3134/sql-editor-warns-about-missing-rls-policy-incorrectly). ## Summary by CodeRabbit * **Tests** * Added comprehensive test coverage for Row Level Security detection across different SQL syntax patterns and clause combinations * **Bug Fixes** * Enhanced Row Level Security detection capabilities in the SQL editor by extending support for additional ALTER TABLE statement syntax variations, improving the accuracy and completeness of security configuration recognition Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com> --- .../SQLEditor/SQLEditor.utils.test.ts | 9 +++++ apps/studio/lib/sql-event-parser.test.ts | 36 +++++++++++++++++++ apps/studio/lib/sql-event-parser.ts | 4 +-- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts b/apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts index 76fe45abe05..d0fc0f6706c 100644 --- a/apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts +++ b/apps/studio/components/interfaces/SQLEditor/SQLEditor.utils.test.ts @@ -414,6 +414,15 @@ describe('SQLEditor.utils:getCreateTablesMissingRLS', () => { expect(getCreateTablesMissingRLS(sql)).toEqual([]) }) + it('does not flag when ALTER TABLE IF EXISTS enables RLS', () => { + const sql = stripIndent` + CREATE TABLE IF NOT EXISTS public."Conversations" (id int8 primary key); + ALTER TABLE IF EXISTS public."Conversations" ENABLE ROW LEVEL SECURITY; + GRANT ALL ON TABLE public."Conversations" TO postgres, anon, authenticated, service_role; + ` + expect(getCreateTablesMissingRLS(sql)).toEqual([]) + }) + it('flags CREATE TEMP TABLE', () => { const result = getCreateTablesMissingRLS('create temp table foo (id int8 primary key);') expect(result).toHaveLength(1) diff --git a/apps/studio/lib/sql-event-parser.test.ts b/apps/studio/lib/sql-event-parser.test.ts index 6575d02dc10..7cb18459faa 100644 --- a/apps/studio/lib/sql-event-parser.test.ts +++ b/apps/studio/lib/sql-event-parser.test.ts @@ -288,6 +288,42 @@ describe('SQL Event Parser', () => { const results = sqlEventParser.getTableEvents('ALTER TABLE users DISABLE ROW LEVEL SECURITY') expect(results).toHaveLength(0) }) + + it('detects ALTER TABLE IF EXISTS ENABLE ROW LEVEL SECURITY', () => { + const results = sqlEventParser.getTableEvents( + 'ALTER TABLE IF EXISTS public."Conversations" ENABLE ROW LEVEL SECURITY' + ) + expect(results).toHaveLength(1) + expect(results[0]).toEqual({ + type: TABLE_EVENT_ACTIONS.TableRLSEnabled, + schema: 'public', + tableName: 'Conversations', + }) + }) + + it('detects ALTER TABLE ONLY ENABLE ROW LEVEL SECURITY', () => { + const results = sqlEventParser.getTableEvents( + 'ALTER TABLE ONLY public.users ENABLE ROW LEVEL SECURITY' + ) + expect(results).toHaveLength(1) + expect(results[0]).toEqual({ + type: TABLE_EVENT_ACTIONS.TableRLSEnabled, + schema: 'public', + tableName: 'users', + }) + }) + + it('detects ALTER TABLE IF EXISTS ONLY ENABLE ROW LEVEL SECURITY', () => { + const results = sqlEventParser.getTableEvents( + 'ALTER TABLE IF EXISTS ONLY public.users ENABLE ROW LEVEL SECURITY' + ) + expect(results).toHaveLength(1) + expect(results[0]).toEqual({ + type: TABLE_EVENT_ACTIONS.TableRLSEnabled, + schema: 'public', + tableName: 'users', + }) + }) }) describe('ReDoS protection', () => { diff --git a/apps/studio/lib/sql-event-parser.ts b/apps/studio/lib/sql-event-parser.ts index 4b01c1652aa..a9023fd7b8b 100644 --- a/apps/studio/lib/sql-event-parser.ts +++ b/apps/studio/lib/sql-event-parser.ts @@ -39,8 +39,8 @@ export class SQLEventParser { { type: TABLE_EVENT_ACTIONS.TableRLSEnabled, patterns: [ - /ALTER\s+TABLE\s+(?(?:"[^"]+"|[\w]+)\.)?(?(?:"(?:[^"]|"")+"|`(?:[^`]|``)+`|[\w]+)).*?ENABLE\s+ROW\s+LEVEL\s+SECURITY/i, - /ALTER\s+TABLE\s+(?(?:"[^"]+"|[\w]+)\.)?(?
(?:"(?:[^"]|"")+"|`(?:[^`]|``)+`|[\w]+)).*?ENABLE\s+RLS/i, + /ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?(?(?:"[^"]+"|[\w]+)\.)?(?
(?:"(?:[^"]|"")+"|`(?:[^`]|``)+`|[\w]+)).*?ENABLE\s+ROW\s+LEVEL\s+SECURITY/i, + /ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:ONLY\s+)?(?(?:"[^"]+"|[\w]+)\.)?(?
(?:"(?:[^"]|"")+"|`(?:[^`]|``)+`|[\w]+)).*?ENABLE\s+RLS/i, ], }, ]