From e50aecc59486cfd1169fb7816da25dc3fc4bd6ec Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Fri, 26 Apr 2024 15:27:56 +0200 Subject: [PATCH] chore: Update the linter with the latest code (#23271) Update the linter with the latest code. --- apps/studio/data/lint/lint-query.ts | 124 ++++++++++++++++++---------- 1 file changed, 81 insertions(+), 43 deletions(-) diff --git a/apps/studio/data/lint/lint-query.ts b/apps/studio/data/lint/lint-query.ts index 5c290d45e79..5135ef2a9d7 100644 --- a/apps/studio/data/lint/lint-query.ts +++ b/apps/studio/data/lint/lint-query.ts @@ -90,22 +90,22 @@ select ) as detail, 'https://supabase.com/docs/guides/database/database-linter?lint=0002_auth_users_exposed' as remediation, jsonb_build_object( - 'schema', 'public', + 'schema', n.nspname, 'name', c.relname, 'type', 'view', 'exposed_to', array_remove(array_agg(DISTINCT case when pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') then 'anon' when pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') then 'authenticated' end), null) ) as metadata, - format('auth_users_exposed_%s_%s', 'public', c.relname) as cache_key + format('auth_users_exposed_%s_%s', n.nspname, c.relname) as cache_key from -- Identify the oid for auth.users - pg_catalog.pg_class auth_users_pg_class + pg_catalog.pg_class auth_users_pg_class join pg_catalog.pg_namespace auth_users_pg_namespace - on auth_users_pg_class.relnamespace = auth_users_pg_namespace.oid - and auth_users_pg_class.relname = 'users' - and auth_users_pg_namespace.nspname = 'auth' - -- Depends on auth.users + on auth_users_pg_class.relnamespace = auth_users_pg_namespace.oid + and auth_users_pg_class.relname = 'users' + and auth_users_pg_namespace.nspname = 'auth' + -- Depends on auth.users join pg_catalog.pg_depend d - on d.refobjid = auth_users_pg_class.oid + on d.refobjid = auth_users_pg_class.oid join pg_catalog.pg_rewrite r on r.oid = d.objid join pg_catalog.pg_class c @@ -116,7 +116,6 @@ from on d.refobjid = pg_class_auth_users.oid where d.deptype = 'n' - and n.nspname = 'public' and ( pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') @@ -132,7 +131,7 @@ where -- Standard View, accessible to anon or authenticated that is security_definer ( c.relkind = 'v' -- v for view - -- Exclude security invoker views + -- Exclude security invoker views and not ( lower(coalesce(c.reloptions::text,'{}'))::text[] && array[ @@ -147,7 +146,7 @@ where -- Standard View, security invoker, but no RLS enabled on auth.users ( c.relkind in ('v') -- v for view - -- is security invoker + -- is security invoker and ( lower(coalesce(c.reloptions::text,'{}'))::text[] && array[ @@ -157,11 +156,13 @@ where 'security_invoker=on' ] ) - and not pg_class_auth_users.relrowsecurity + and not pg_class_auth_users.relrowsecurity ) ) group by - c.relname, c.oid) + n.nspname, + c.relname, + c.oid) union all ( with policies as ( @@ -219,18 +220,38 @@ where '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault' ) and ( + -- Example: auth.uid() ( - -- Example: auth.uid() - qual ~ '(auth)\\.(uid|jwt|role|email)\\(\\)' - -- Example: select auth.uid() - and lower(qual) !~ 'select\\s+(auth)\\.(uid|jwt|role|email)\\(\\)' + qual like '%auth.uid()%' + and lower(qual) not like '%select auth.uid()%' ) - or - ( - -- Example: auth.uid() - with_check ~ '(auth)\\.(uid|jwt|role|email)\\(\\)' - -- Example: select auth.uid() - and lower(with_check) !~ 'select\\s+(auth)\\.(uid|jwt|role|email)\\(\\)' + or ( + qual like '%auth.jwt()%' + and lower(qual) not like '%select auth.jwt()%' + ) + or ( + qual like '%auth.role()%' + and lower(qual) not like '%select auth.role()%' + ) + or ( + qual like '%auth.email()%' + and lower(qual) not like '%select auth.email()%' + ) + or ( + with_check like '%auth.uid()%' + and lower(with_check) not like '%select auth.uid()%' + ) + or ( + with_check like '%auth.jwt()%' + and lower(with_check) not like '%select auth.jwt()%' + ) + or ( + with_check like '%auth.role()%' + and lower(with_check) not like '%select auth.role()%' + ) + or ( + with_check like '%auth.email()%' + and lower(with_check) not like '%select auth.email()%' ) )) union all @@ -377,6 +398,7 @@ from ) act(cmd) where c.relkind = 'r' -- regular tables + and p.polpermissive -- policy is permissive and n.nspname not in ( '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault' ) @@ -568,17 +590,23 @@ from and dep.deptype = 'e' where c.relkind = 'v' - and n.nspname = 'public' + and ( + pg_catalog.has_schema_privilege('anon', n.nspname, 'USAGE') + or pg_catalog.has_schema_privilege('authenticated', n.nspname, 'USAGE') + ) + and n.nspname not in ( + 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgsodium', 'pgsodium_masks', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'storage', 'supabase_functions', 'supabase_migrations', 'vault' + ) and dep.objid is null -- exclude views owned by extensions - and not ( - lower(coalesce(c.reloptions::text,'{}'))::text[] - && array[ - 'security_invoker=1', - 'security_invoker=true', - 'security_invoker=yes', - 'security_invoker=on' - ] - )) + and not ( + lower(coalesce(c.reloptions::text,'{}'))::text[] + && array[ + 'security_invoker=1', + 'security_invoker=true', + 'security_invoker=yes', + 'security_invoker=on' + ] + )) union all ( select @@ -648,12 +676,18 @@ from on c.relnamespace = n.oid where c.relkind = 'r' -- regular tables - and n.nspname = 'public' -- RLS is disabled - and not c.relrowsecurity) + and not c.relrowsecurity + and ( + pg_catalog.has_schema_privilege('anon', n.nspname, 'USAGE') + or pg_catalog.has_schema_privilege('authenticated', n.nspname, 'USAGE') + ) + and n.nspname not in ( + '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault' + )) union all ( -select +select 'extension_in_public' as name, 'WARN' as level, 'EXTERNAL' as facing, @@ -679,6 +713,10 @@ where -- plpgsql is installed by default in public and outside user control -- confirmed safe pe.extname not in ('plpgsql') + -- Scoping this to public is not optimal. Ideally we would use the postgres + -- search path. That currently isn't available via SQL. In other lints + -- we have used has_schema_privilege('anon', 'extensions', 'USAGE') but that + -- is not appropriate here as it would evaluate true for the extensions schema and pe.extnamespace::regnamespace::text = 'public') union all ( @@ -726,13 +764,13 @@ where '_timescaledb_internal', 'auth', 'cron', 'extensions', 'graphql', 'graphql_public', 'information_schema', 'net', 'pgroonga', 'pgsodium', 'pgsodium_masks', 'pgtle', 'pgbouncer', 'pg_catalog', 'pgtle', 'realtime', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault' ) and ( - -- Example: auth.jwt() -> 'user_metadata' - -- False positives are possible, but it isn't practical to string match - -- If false positive rate is too high, this expression can iterate - qual like '%auth.jwt()%user_metadata%' - or qual like '%current_setting(%request.jwt.claims%)%user_metadata%' - or with_check like '%auth.jwt()%user_metadata%' - or with_check like '%current_setting(%request.jwt.claims%)%user_metadata%' + -- Example: auth.jwt() -> 'user_metadata' + -- False positives are possible, but it isn't practical to string match + -- If false positive rate is too high, this expression can iterate + qual like '%auth.jwt()%user_metadata%' + or qual like '%current_setting(%request.jwt.claims%)%user_metadata%' + or with_check like '%auth.jwt()%user_metadata%' + or with_check like '%current_setting(%request.jwt.claims%)%user_metadata%' ))`.trim() // Array of all lint rules we handle right now.