mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
1 parent
c9467f592c
commit
f9db8f3482
1 file changed
+78
-17
@@ -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 = [
|
||||
|
||||
Reference in new issue
Block a user