From f9db8f34828a2f8fcd686131326d326a8a36a905 Mon Sep 17 00:00:00 2001 From: Oliver Rice Date: Mon, 13 May 2024 14:13:15 -0500 Subject: [PATCH] Update lints (#26263) update lints --- apps/studio/data/lint/lint-query.ts | 95 +++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/apps/studio/data/lint/lint-query.ts b/apps/studio/data/lint/lint-query.ts index 58bbdf31c72..220df3a12c2 100644 --- a/apps/studio/data/lint/lint-query.ts +++ b/apps/studio/data/lint/lint-query.ts @@ -5,6 +5,7 @@ import { lintKeys } from './keys' import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' export const LINT_SQL = /* SQL */ `set local search_path = ''; + ( with foreign_keys as ( select @@ -39,6 +40,7 @@ index_ as ( ) select 'unindexed_foreign_keys' as name, + 'Unindexed foreign keys' as title, 'INFO' as level, 'EXTERNAL' as facing, array['PERFORMANCE'] as categories, @@ -69,7 +71,7 @@ from where idx.index_ is null and fk.schema_name 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 dep.objid is null -- exclude tables owned by extensions order by @@ -80,10 +82,11 @@ union all ( select 'auth_users_exposed' as name, + 'Exposed Auth Users' as title, 'ERROR' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, - 'Detects if auth.users is exposed to anon or authenticated roles via a view or materialized view in the public schema, potentially compromising user data security.' as description, + 'Detects if auth.users is exposed to anon or authenticated roles via a view or materialized view in schemas exposed to PostgREST, potentially compromising user data security.' as description, format( 'View/Materialized View "%s" in the public schema may expose \`auth.users\` data to anon or authenticated roles.', c.relname @@ -120,6 +123,7 @@ where pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') ) + and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ','))))) -- Exclude self and c.relname <> '0002_auth_users_exposed' -- There are 3 insecure configurations @@ -195,6 +199,7 @@ with policies as ( ) select 'auth_rls_initplan' as name, + 'Auth RLS Initialization Plan' as title, 'WARN' as level, 'EXTERNAL' as facing, array['PERFORMANCE'] as categories, @@ -217,7 +222,7 @@ from where is_rls_active and schema_name 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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() @@ -258,6 +263,7 @@ union all ( select 'no_primary_key' as name, + 'No Primary Key' as title, 'INFO' as level, 'EXTERNAL' as facing, array['PERFORMANCE'] as categories, @@ -290,7 +296,7 @@ from where pgc.relkind = 'r' -- regular tables and pgns.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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 dep.objid is null -- exclude tables owned by extensions group by @@ -303,6 +309,7 @@ union all ( select 'unused_index' as name, + 'Unused Index' as title, 'INFO' as level, 'EXTERNAL' as facing, array['PERFORMANCE'] as categories, @@ -339,12 +346,13 @@ where and not pi.indisprimary and dep.objid is null -- exclude tables owned by extensions and psui.schemaname 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 'multiple_permissive_policies' as name, + 'Multiple Permissive Policies' as title, 'WARN' as level, 'EXTERNAL' as facing, array['PERFORMANCE'] as categories, @@ -400,7 +408,7 @@ 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 r.rolname not like 'pg_%' and r.rolname not like 'supabase%admin' @@ -417,6 +425,7 @@ union all ( select 'policy_exists_rls_disabled' as name, + 'Policy Exists RLS Disabled' as title, 'ERROR' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, @@ -450,7 +459,7 @@ from where c.relkind = 'r' -- regular tables 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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' ) -- RLS is disabled and not c.relrowsecurity @@ -462,6 +471,7 @@ union all ( select 'rls_enabled_no_policy' as name, + 'RLS Enabled No Policy' as title, 'INFO' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, @@ -494,7 +504,7 @@ from where c.relkind = 'r' -- regular tables 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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' ) -- RLS is enabled and c.relrowsecurity @@ -507,6 +517,7 @@ union all ( select 'duplicate_index' as name, + 'Duplicate Index' as title, 'WARN' as level, 'EXTERNAL' as facing, array['PERFORMANCE'] as categories, @@ -547,7 +558,7 @@ from where c.relkind in ('r', 'm') -- tables and materialized views 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 dep.objid is null -- exclude tables owned by extensions group by @@ -561,12 +572,13 @@ union all ( select 'security_definer_view' as name, + 'Security Definer View' as title, 'ERROR' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, - 'Detects views that are SECURITY DEFINER meaning that they ignore row level security (RLS) policies.' as description, + 'Detects views defined with the SECURITY DEFINER property. These views enforce Postgres permissions and row level security policies (RLS) of the view creator, rather than that of the querying user' as description, format( - 'View \`%s.%s\` is SECURITY DEFINER', + 'View \`%s.%s\` is defined with the SECURITY DEFINER property', n.nspname, c.relname ) as detail, @@ -594,8 +606,9 @@ where pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') ) + and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ','))))) 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 dep.objid is null -- exclude views owned by extensions and not ( @@ -611,6 +624,7 @@ union all ( select 'function_search_path_mutable' as name, + 'Function Search Path Mutable' as title, 'WARN' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, @@ -641,7 +655,7 @@ from and dep.deptype = 'e' where 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 dep.objid is null -- exclude functions owned by extensions -- Search path not set to '' @@ -650,10 +664,11 @@ union all ( select 'rls_disabled_in_public' as name, + 'RLS Disabled in Public' as title, 'ERROR' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, - 'Detects cases where row level security (RLS) has not been enabled on a table in the \`public\` schema.' as description, + 'Detects cases where row level security (RLS) has not been enabled on tables in schemas exposed to PostgREST' as description, format( 'Table \`%s.%s\` is public, but RLS has not been enabled.', n.nspname, @@ -682,13 +697,15 @@ where pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') ) + and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ','))))) 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 'extension_in_public' as name, + 'Extension in Public' as title, 'WARN' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, @@ -740,6 +757,7 @@ with policies as ( ) select 'rls_references_user_metadata' as name, + 'RLS references user metadata' as title, 'ERROR' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, @@ -761,7 +779,7 @@ from policies where schema_name 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' + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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' @@ -771,7 +789,50 @@ where 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() + )) +union all +( +select + 'materialized_view_in_api' as name, + 'Materialized View in API' as title, + 'WARN' as level, + 'EXTERNAL' as facing, + array['SECURITY'] as categories, + 'Detects materialized views that are potentially accessible over the Data APIs.' as description, + format( + 'Materialized view \`%s.%s\` is selectable by anon or authenticated roles', + n.nspname, + c.relname + ) as detail, + 'https://supabase.com/docs/guides/database/database-linter?lint=0016_materialized_view_in_api' as remediation, + jsonb_build_object( + 'schema', n.nspname, + 'name', c.relname, + 'type', 'materialized view' + ) as metadata, + format( + 'materialized_view_in_api_%s_%s', + n.nspname, + c.relname + ) as cache_key +from + pg_catalog.pg_class c + join pg_catalog.pg_namespace n + on n.oid = c.relnamespace + left join pg_catalog.pg_depend dep + on c.oid = dep.objid + and dep.deptype = 'e' +where + c.relkind = 'm' + and ( + pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') + or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') + ) + and n.nspname = any(array(select trim(unnest(string_to_array(current_setting('pgrst.db_schemas', 't'), ','))))) + and n.nspname not in ( + '_timescaledb_cache', '_timescaledb_catalog', '_timescaledb_config', '_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 dep.objid is null)`.trim() // Array of all lint rules we handle right now. export const LINT_TYPES = [