From bee6afe899dfd31c661ee2a6814b138ae134e1e7 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 9 Aug 2024 12:07:10 -0230 Subject: [PATCH] Chore/update local lints (#28421) * Update lints * Add new lint type, update api types * Update lints again * Update types --- .../interfaces/Linter/Linter.utils.tsx | 10 +++ .../pages/api/projects/[ref]/run-lints.ts | 71 +++++++++++++------ packages/api-types/types/api.d.ts | 56 +++++++++++++-- 3 files changed, 110 insertions(+), 27 deletions(-) diff --git a/apps/studio/components/interfaces/Linter/Linter.utils.tsx b/apps/studio/components/interfaces/Linter/Linter.utils.tsx index 5eb57131661..24e11f9b2ee 100644 --- a/apps/studio/components/interfaces/Linter/Linter.utils.tsx +++ b/apps/studio/components/interfaces/Linter/Linter.utils.tsx @@ -178,6 +178,16 @@ export const lintInfoMap: LintInfo[] = [ docsLink: 'https://supabase.com/docs/guides/database/database-linter?lint=0017_foreign_table_in_api', }, + { + name: 'unsupported_reg_types', + title: 'Unsupported reg types', + icon: , + link: () => + `https://supabase.com/docs/guides/database/database-advisors?lint=0018_unsupported_reg_types&queryGroups=lint`, + linkText: 'View docs', + docsLink: + 'https://supabase.com/docs/guides/database/database-advisors?lint=0018_unsupported_reg_types&queryGroups=lint', + }, ] export const LintCTA = ({ diff --git a/apps/studio/pages/api/projects/[ref]/run-lints.ts b/apps/studio/pages/api/projects/[ref]/run-lints.ts index f5340fc913a..5bd9eea3432 100644 --- a/apps/studio/pages/api/projects/[ref]/run-lints.ts +++ b/apps/studio/pages/api/projects/[ref]/run-lints.ts @@ -41,6 +41,7 @@ ${query} ` export const LINT_SQL = /* SQL */ `set local search_path = ''; + ( with foreign_keys as ( select @@ -131,7 +132,6 @@ select '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', n.nspname, c.relname) as cache_key @@ -257,8 +257,9 @@ from policies where is_rls_active + -- NOTE: does not include realtime in support of monitoring policies on realtime.messages and schema_name 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' + '_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', 'repack', 'storage', 'supabase_functions', 'supabase_migrations', 'tiger', 'topology', 'vault' ) and ( -- Example: auth.uid() @@ -368,6 +369,7 @@ select psui.relname, psui.indexrelname ) as cache_key + from pg_catalog.pg_stat_user_indexes psui join pg_catalog.pg_index pi @@ -410,7 +412,6 @@ select 'multiple_permissive_policies_%s_%s_%s_%s', n.nspname, c.relname, - r.rolname, act.cmd ) as cache_key @@ -458,7 +459,6 @@ group by having count(1) > 1) union all - ( select 'policy_exists_rls_disabled' as name, @@ -470,12 +470,12 @@ select format( 'Table \`%s.%s\` has RLS policies but RLS is not enabled on the table. Policies include %s.', n.nspname, - c.relname, array_agg(p.polname order by p.polname) ) as detail, 'https://supabase.com/docs/guides/database/database-linter?lint=0007_policy_exists_rls_disabled' as remediation, jsonb_build_object( + 'schema', n.nspname, 'name', c.relname, 'type', 'table' ) as metadata, @@ -602,10 +602,8 @@ group by n.nspname, c.relkind, c.relname, - replace(pi.indexdef, pi.indexname, '') having - count(*) > 1) union all ( @@ -645,6 +643,7 @@ where pg_catalog.has_table_privilege('anon', c.oid, 'SELECT') or pg_catalog.has_table_privilege('authenticated', c.oid, 'SELECT') ) + and substring(pg_catalog.version() from 'PostgreSQL ([0-9]+)') >= '15' -- security invoker was added in pg15 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' @@ -667,7 +666,7 @@ select 'WARN' as level, 'EXTERNAL' as facing, array['SECURITY'] as categories, - 'Detects functions with a mutable search_path parameter which could fail to execute successfully for some roles.' as description, + 'Detects functions where the search_path parameter is not set to an empty string.' as description, format( 'Function \`%s.%s\` has a role mutable search_path', n.nspname, @@ -738,9 +737,7 @@ where ) 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' - )) union all ( @@ -753,13 +750,11 @@ select 'Detects extensions installed in the \`public\` schema.' as description, format( 'Extension \`%s\` is installed in the public schema. Move it to another schema.', - pe.extname ) as detail, 'https://supabase.com/docs/guides/database/database-linter?lint=0014_extension_in_public' as remediation, jsonb_build_object( 'schema', pe.extnamespace::regnamespace, - 'name', pe.extname, 'type', 'extension' ) as metadata, @@ -777,11 +772,9 @@ where -- 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 ( - with policies as ( select nsp.nspname as schema_name, @@ -817,12 +810,9 @@ select jsonb_build_object( 'schema', schema_name, 'name', table_name, - 'type', 'table' ) as metadata, - format('rls_references_user_metadata_%s_%s_%s', schema_name, table_name, policy_name) as cache_key - from policies where @@ -841,11 +831,9 @@ where 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 accessible over the Data APIs.' as description, @@ -925,4 +913,47 @@ where 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() + and dep.objid is null) +union all +( +select + 'unsupported_reg_types' as name, + 'Unsupported reg types' as title, + 'WARN' as level, + 'EXTERNAL' as facing, + array['SECURITY'] as categories, + 'Identifies columns using unsupported reg* types outside pg_catalog schema, which prevents database upgrades using pg_upgrade.' as description, + format( + 'Table \`%s.%s\` has a column \`%s\` with unsupported reg* type \`%s\`.', + n.nspname, + c.relname, + a.attname, + t.typname + ) as detail, + 'https://supabase.com/docs/guides/database/database-linter?lint=unsupported_reg_types' as remediation, + jsonb_build_object( + 'schema', n.nspname, + 'name', c.relname, + 'column', a.attname, + 'type', 'table' + ) as metadata, + format( + 'unsupported_reg_types_%s_%s_%s', + n.nspname, + c.relname, + a.attname + ) AS cache_key +from + pg_catalog.pg_attribute a + join pg_catalog.pg_class c + on a.attrelid = c.oid + join pg_catalog.pg_namespace n + on c.relnamespace = n.oid + join pg_catalog.pg_type t + on a.atttypid = t.oid + join pg_catalog.pg_namespace tn + on t.typnamespace = tn.oid +where + tn.nspname = 'pg_catalog' + and t.typname in ('regcollation', 'regconfig', 'regdictionary', 'regnamespace', 'regoper', 'regoperator', 'regproc', 'regprocedure') + and n.nspname not in ('pg_catalog', 'information_schema', 'pgsodium'))`.trim() diff --git a/packages/api-types/types/api.d.ts b/packages/api-types/types/api.d.ts index e1b4e0c2b73..24fa6823934 100644 --- a/packages/api-types/types/api.d.ts +++ b/packages/api-types/types/api.d.ts @@ -2131,6 +2131,7 @@ export interface components { | 'pitr_14' | 'pitr_28' | 'ipv4_default' + | 'auth_mfa_phone_default' AmiSearchOptions: { search_tags?: Record } @@ -2291,6 +2292,11 @@ export interface components { mailer_templates_reauthentication_content: string | null mailer_templates_recovery_content: string | null mfa_max_enrolled_factors: number | null + mfa_phone_enroll_enabled: boolean | null + mfa_phone_max_frequency: number | null + mfa_phone_otp_length: number + mfa_phone_template: string | null + mfa_phone_verify_enabled: boolean | null mfa_totp_enroll_enabled: boolean | null mfa_totp_verify_enabled: boolean | null password_hibp_enabled: boolean | null @@ -2692,10 +2698,15 @@ export interface components { data_api_use_api_schema?: boolean db_pass: string db_pricing_tier_id?: string + /** + * @description Provider region description + * @example Southeast Asia (Singapore) + */ db_region: string db_sql?: string desired_instance_size?: components['schemas']['DesiredInstanceSize'] name: string + /** @deprecated */ org_id?: number organization_slug?: string } @@ -3465,6 +3476,11 @@ export interface components { MAILER_TEMPLATES_REAUTHENTICATION_CONTENT: string MAILER_TEMPLATES_RECOVERY_CONTENT: string MFA_MAX_ENROLLED_FACTORS: number + MFA_PHONE_ENROLL_ENABLED: boolean + MFA_PHONE_MAX_FREQUENCY: number + MFA_PHONE_OTP_LENGTH: number + MFA_PHONE_TEMPLATE: string + MFA_PHONE_VERIFY_ENABLED: boolean MFA_TOTP_ENROLL_ENABLED: boolean MFA_TOTP_VERIFY_ENABLED: boolean PASSWORD_HIBP_ENABLED: boolean @@ -3614,11 +3630,11 @@ export interface components { id: string invoice_pdf: string number: string + payment_attempted: boolean period_end: number status: string subscription: string | null subtotal: number - payment_attempted: boolean } JoinResponse: { billing_email: string @@ -3956,7 +3972,14 @@ export interface components { | 'STORAGE_IMAGES_TRANSFORMED' | 'REALTIME_MESSAGE_COUNT' | 'REALTIME_PEAK_CONNECTIONS' - | 'DISK_SIZE_GB_HOURS' + | 'DISK_SIZE_GB_HOURS_GP3' + | 'DISK_SIZE_GB_HOURS_IO2' + | 'AUTH_MFA_PHONE' + | 'LOG_DRAIN_EVENTS' + | 'MONTHLY_ACTIVE_THIRD_PARTY_USERS' + | 'DISK_THROUGHPUT_GP3' + | 'DISK_IOPS_GP3' + | 'DISK_IOPS_IO2' | 'COMPUTE_HOURS_BRANCH' | 'COMPUTE_HOURS_XS' | 'COMPUTE_HOURS_SM' @@ -3973,12 +3996,13 @@ export interface components { | 'PITR_14' | 'PITR_28' | 'IPV4' + | 'LOG_DRAIN' pricing_free_units?: number pricing_package_price?: number pricing_package_size?: number pricing_per_unit_price?: number /** @enum {string} */ - pricing_strategy: 'UNIT' | 'PACKAGE' | 'NONE' + pricing_strategy: 'UNIT' | 'PACKAGE' | 'TIERED' | 'NONE' project_allocations: components['schemas']['ProjectAllocation'][] unit_price_desc: string unlimited: boolean @@ -4338,7 +4362,7 @@ export interface components { selected_addons: components['schemas']['SelectedAddonResponse'][] } /** @enum {string} */ - ProjectAddonType: 'custom_domain' | 'compute_instance' | 'pitr' | 'ipv4' + ProjectAddonType: 'custom_domain' | 'compute_instance' | 'pitr' | 'ipv4' | 'auth_mfa_phone' /** @enum {string} */ ProjectAddonVariantPricingType: 'fixed' | 'usage' ProjectAddonVariantResponse: { @@ -5424,6 +5448,11 @@ export interface components { mailer_templates_reauthentication_content?: string mailer_templates_recovery_content?: string mfa_max_enrolled_factors?: number + mfa_phone_enroll_enabled?: boolean + mfa_phone_max_frequency?: number + mfa_phone_otp_length?: number + mfa_phone_template?: string + mfa_phone_verify_enabled?: boolean mfa_totp_enroll_enabled?: boolean mfa_totp_verify_enabled?: boolean password_hibp_enabled?: boolean @@ -5666,6 +5695,11 @@ export interface components { MAILER_TEMPLATES_REAUTHENTICATION_CONTENT?: string MAILER_TEMPLATES_RECOVERY_CONTENT?: string MFA_MAX_ENROLLED_FACTORS?: number + MFA_PHONE_ENROLL_ENABLED?: boolean + MFA_PHONE_MAX_FREQUENCY?: number + MFA_PHONE_OTP_LENGTH?: number + MFA_PHONE_TEMPLATE?: string + MFA_PHONE_VERIFY_ENABLED?: boolean MFA_TOTP_ENROLL_ENABLED?: boolean MFA_TOTP_VERIFY_ENABLED?: boolean PASSWORD_HIBP_ENABLED?: boolean @@ -7848,7 +7882,14 @@ export interface operations { | 'STORAGE_IMAGES_TRANSFORMED' | 'REALTIME_MESSAGE_COUNT' | 'REALTIME_PEAK_CONNECTIONS' - | 'DISK_SIZE_GB_HOURS' + | 'DISK_SIZE_GB_HOURS_GP3' + | 'DISK_SIZE_GB_HOURS_IO2' + | 'AUTH_MFA_PHONE' + | 'LOG_DRAIN_EVENTS' + | 'MONTHLY_ACTIVE_THIRD_PARTY_USERS' + | 'DISK_THROUGHPUT_GP3' + | 'DISK_IOPS_GP3' + | 'DISK_IOPS_IO2' | 'COMPUTE_HOURS_BRANCH' | 'COMPUTE_HOURS_XS' | 'COMPUTE_HOURS_SM' @@ -7865,6 +7906,7 @@ export interface operations { | 'PITR_14' | 'PITR_28' | 'IPV4' + | 'LOG_DRAIN' interval: string endDate: string startDate: string @@ -11673,8 +11715,8 @@ export interface operations { /** Gets the count of a user's content by type */ ContentController_getContentCount: { parameters: { - query: { - type: string + query?: { + type?: string } path: { /** @description Project ref */