diff --git a/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx b/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx
index 4fb98099095..8fe6fe053fd 100644
--- a/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx
+++ b/apps/studio/components/interfaces/Database/Functions/FunctionsList/FunctionsList.tsx
@@ -2,9 +2,10 @@ import * as Tooltip from '@radix-ui/react-tooltip'
import type { PostgresFunction } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { noop, partition } from 'lodash'
-import { useState } from 'react'
+import { useRouter } from 'next/router'
import { Button, IconSearch, Input } from 'ui'
+import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import Table from 'components/to-be-cleaned/Table'
@@ -15,6 +16,7 @@ import { useDatabaseFunctionsQuery } from 'data/database-functions/database-func
import { useSchemasQuery } from 'data/database/schemas-query'
import { useCheckPermissions } from 'hooks'
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
+import { useEffect } from 'react'
import ProtectedSchemaWarning from '../../ProtectedSchemaWarning'
import FunctionList from './FunctionList'
@@ -30,8 +32,33 @@ const FunctionsList = ({
deleteFunction = noop,
}: FunctionsListProps) => {
const { project } = useProjectContext()
- const [selectedSchema, setSelectedSchema] = useState('public')
- const [filterString, setFilterString] = useState('')
+ const router = useRouter()
+ const { schema, search } = useParams()
+ const selectedSchema = schema ?? 'public'
+ const filterString = search ?? ''
+
+ const setSelectedSchema = (s: string) => {
+ const url = new URL(document.URL)
+ url.searchParams.delete('search')
+ url.searchParams.set('schema', s)
+ router.push(url)
+ }
+ const setFilterString = (str: string) => {
+ const url = new URL(document.URL)
+ if (str === '') {
+ url.searchParams.delete('search')
+ } else {
+ url.searchParams.set('search', str)
+ }
+ router.push(url)
+ }
+
+ // update the url to point to public schema
+ useEffect(() => {
+ if (schema !== selectedSchema) {
+ setSelectedSchema(selectedSchema)
+ }
+ }, [])
const canCreateFunctions = useCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_WRITE,
@@ -45,8 +72,8 @@ const FunctionsList = ({
const [protectedSchemas] = partition(schemas ?? [], (schema) =>
EXCLUDED_SCHEMAS.includes(schema?.name ?? '')
)
- const schema = schemas?.find((schema) => schema.name === selectedSchema)
- const isLocked = protectedSchemas.some((s) => s.id === schema?.id)
+ const foundSchema = schemas?.find((schema) => schema.name === selectedSchema)
+ const isLocked = protectedSchemas.some((s) => s.id === foundSchema?.id)
const {
data: functions,
diff --git a/apps/studio/components/interfaces/Reports/ReportLints.utils.tsx b/apps/studio/components/interfaces/Reports/ReportLints.utils.tsx
index 14e26da19fb..b044d3148a3 100644
--- a/apps/studio/components/interfaces/Reports/ReportLints.utils.tsx
+++ b/apps/studio/components/interfaces/Reports/ReportLints.utils.tsx
@@ -20,6 +20,18 @@ export function getHumanReadableTitle(title: LINT_TYPES) {
return 'Unused Index'
case 'multiple_permissive_policies':
return 'Multiple Permissive Policies'
+ case 'function_search_path_mutable':
+ return 'Function Search Path Mutable'
+ case 'rls_enabled_no_policy':
+ return 'RLS Enabled No Policy'
+ case 'policy_exists_rls_disabled':
+ return 'Policy Exists RLS Disabled'
+ case 'rls_disabled_in_public':
+ return 'RLS Disabled in Public'
+ case 'security_definer_view':
+ return 'Security Definer View'
+ case 'duplicate_index':
+ return 'Duplicate Index'
default:
assertUnreachable(title)
throw new Error('This case should never be reached')
@@ -85,6 +97,18 @@ export const LintCTA = ({
)
+ case 'duplicate_index':
+ return (
+
+ )
case 'multiple_permissive_policies':
return (
)
+ case 'function_search_path_mutable':
+ return (
+
+ )
+ case 'rls_enabled_no_policy':
+ return (
+
+ )
+ case 'policy_exists_rls_disabled':
+ return (
+
+ )
+ case 'rls_disabled_in_public':
+ return (
+
+ )
+ case 'security_definer_view':
+ // we don't have a good place to send the user to check out a view
+ return <>>
+
default:
assertUnreachable(title)
return <>>
diff --git a/apps/studio/components/interfaces/Reports/ReportLintsTableRow.tsx b/apps/studio/components/interfaces/Reports/ReportLintsTableRow.tsx
index 7d31413a126..ff7b1ea23ec 100644
--- a/apps/studio/components/interfaces/Reports/ReportLintsTableRow.tsx
+++ b/apps/studio/components/interfaces/Reports/ReportLintsTableRow.tsx
@@ -1,19 +1,9 @@
-import { Eye, EyeIcon, EyeOff, HelpCircle, Table2 } from 'lucide-react'
-import { useState } from 'react'
+import { Eye, HelpCircle, Table2 } from 'lucide-react'
import { useParams } from 'common'
import Table from 'components/to-be-cleaned/Table'
import { LINT_TYPES, Lint } from 'data/lint/lint-query'
-import { useLocalStorageQuery } from 'hooks'
-import { LOCAL_STORAGE_KEYS } from 'lib/constants'
-import {
- Badge,
- Button,
- Modal,
- TooltipContent_Shadcn_,
- TooltipTrigger_Shadcn_,
- Tooltip_Shadcn_,
-} from 'ui'
+import { Badge, TooltipContent_Shadcn_, TooltipTrigger_Shadcn_, Tooltip_Shadcn_ } from 'ui'
import { Markdown } from '../Markdown'
import { LintCTA, getHumanReadableTitle } from './ReportLints.utils'
@@ -23,33 +13,35 @@ type ReportLintsTableRowProps = {
const ReportLintsTableRow = ({ lint }: ReportLintsTableRowProps) => {
const { ref } = useParams()
- const [selectedLint, setSelectedLint] = useState(null)
+ // TODO: Comment out functionality for ignoring lints because it relied on local storage. Will revisit later.
+ // const [selectedLint, setSelectedLint] = useState(null)
- const [lintIgnoreList, setLintIgnoreList] = useLocalStorageQuery(
- LOCAL_STORAGE_KEYS.PROJECT_LINT_IGNORE_LIST,
- []
- )
- const isIgnored = lintIgnoreList.includes(lint.cache_key)
+ // const [lintIgnoreList, setLintIgnoreList] = useLocalStorageQuery(
+ // LOCAL_STORAGE_KEYS.PROJECT_LINT_IGNORE_LIST,
+ // []
+ // )
+ // const isIgnored = lintIgnoreList.includes(lint.cache_key)
// if the lint type can't be handled (there's no CTA text defined), don't render it
if (!LINT_TYPES.includes(lint.name)) {
+ console.log('Unhandled lint type:', lint.name)
return null
}
- const toggleLintIgnore = () => {
- let currentIgnoreList = []
- const cacheKey = lint.cache_key
+ // const toggleLintIgnore = () => {
+ // let currentIgnoreList = []
+ // const cacheKey = lint.cache_key
- // Check if the cacheKey exists in the array and ignore or unignore it
- const index = lintIgnoreList.indexOf(cacheKey)
- if (index !== -1) {
- currentIgnoreList = lintIgnoreList.filter((l) => l !== cacheKey)
- } else {
- currentIgnoreList = lintIgnoreList.concat(cacheKey)
- }
- setLintIgnoreList(currentIgnoreList)
- setSelectedLint(null)
- }
+ // // Check if the cacheKey exists in the array and ignore or unignore it
+ // const index = lintIgnoreList.indexOf(cacheKey)
+ // if (index !== -1) {
+ // currentIgnoreList = lintIgnoreList.filter((l) => l !== cacheKey)
+ // } else {
+ // currentIgnoreList = lintIgnoreList.concat(cacheKey)
+ // }
+ // setLintIgnoreList(currentIgnoreList)
+ // setSelectedLint(null)
+ // }
return (
<>
@@ -109,7 +101,7 @@ const ReportLintsTableRow = ({ lint }: ReportLintsTableRowProps) => {
)}
{lint.remediation && (
- You can read more about this lint rule and the best ways to remedy it{' '}
+ You can read more about this lint rule and ways to remedy it{' '}
{
-
+ {/*
+ */}
- {
-
+ */}
>
)
}
diff --git a/apps/studio/components/ui/FilterPopover.tsx b/apps/studio/components/ui/FilterPopover.tsx
index 8ef28355f7b..3bf02b2dc31 100644
--- a/apps/studio/components/ui/FilterPopover.tsx
+++ b/apps/studio/components/ui/FilterPopover.tsx
@@ -40,9 +40,10 @@ export const FilterPopover = ({
header={
+
7 ? 'h-[205px]' : ''}>
{
const value = event.target.value
diff --git a/apps/studio/data/lint/lint-query.ts b/apps/studio/data/lint/lint-query.ts
index fbee6eca9b0..69bb874caa6 100644
--- a/apps/studio/data/lint/lint-query.ts
+++ b/apps/studio/data/lint/lint-query.ts
@@ -10,10 +10,10 @@ with foreign_keys as (
ct.conname as fkey_name,
ct.conkey col_attnums
from
- pg_constraint ct
- join pg_class cl -- fkey owning table
+ pg_catalog.pg_constraint ct
+ join pg_catalog.pg_class cl -- fkey owning table
on ct.conrelid = cl.oid
- left join pg_depend d
+ left join pg_catalog.pg_depend d
on d.objid = cl.oid
and d.deptype = 'e'
where
@@ -29,7 +29,7 @@ index_ as (
indexrelid::regclass as index_,
string_to_array(indkey::text, ' ')::smallint[] as col_attnums
from
- pg_index
+ pg_catalog.pg_index
where
indisvalid
)
@@ -42,7 +42,6 @@ select
'Table \`%s.%s\` has a foreign key \`%s\` without a covering index. This can lead to suboptimal query performance.',
fk.schema_,
fk.table_,
- fk.table_,
fk.fkey_name
) as detail,
'https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys' as remediation,
@@ -53,7 +52,7 @@ select
'fkey_name', fk.fkey_name,
'fkey_columns', fk.col_attnums
) as metadata,
- format('0001_unindexed_foreign_keys_%s_%s_%s', fk.schema_, fk.table_, fk.fkey_name) as cache_key
+ format('unindexed_foreign_keys_%s_%s_%s', fk.schema_, fk.table_, fk.fkey_name) as cache_key
from
foreign_keys fk
left join index_ idx
@@ -61,6 +60,9 @@ from
and fk.col_attnums = idx.col_attnums
where
idx.index_ is null
+ and fk.schema_::text not in (
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
+ )
order by
fk.table_,
fk.fkey_name)
@@ -84,17 +86,25 @@ select
) as metadata,
format('auth_users_exposed_%s_%s', 'public', c.relname) as cache_key
from
- pg_depend d
- join pg_rewrite r
+ -- Identify the oid for auth.users
+ 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
+ join pg_catalog.pg_depend d
+ on d.refobjid = auth_users_pg_class.oid
+ join pg_catalog.pg_rewrite r
on r.oid = d.objid
- join pg_class c
+ join pg_catalog.pg_class c
on c.oid = r.ev_class
- join pg_namespace n
+ join pg_catalog.pg_namespace n
on n.oid = c.relnamespace
+ join pg_catalog.pg_class pg_class_auth_users
+ on d.refobjid = pg_class_auth_users.oid
where
- d.refobjid = 'auth.users'::regclass
- and d.deptype = 'n'
- and c.relkind in ('v', 'm') -- v for view, m for materialized view
+ d.deptype = 'n'
and n.nspname = 'public'
and (
pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')
@@ -102,6 +112,43 @@ where
)
-- Exclude self
and c.relname <> '0002_auth_users_exposed'
+ -- There are 3 insecure configurations
+ and
+ (
+ -- Materialized views don't support RLS so this is insecure by default
+ (c.relkind in ('m')) -- m for materialized view
+ or
+ -- Standard View, accessible to anon or authenticated that is security_definer
+ (
+ c.relkind = 'v' -- v for view
+ -- Exclude security invoker views
+ and not (
+ lower(coalesce(c.reloptions::text,'{}'))::text[]
+ && array[
+ 'security_invoker=1',
+ 'security_invoker=true',
+ 'security_invoker=yes',
+ 'security_invoker=on'
+ ]
+ )
+ )
+ or
+ -- Standard View, security invoker, but no RLS enabled on auth.users
+ (
+ c.relkind in ('v') -- v for view
+ -- is security invoker
+ and (
+ lower(coalesce(c.reloptions::text,'{}'))::text[]
+ && array[
+ 'security_invoker=1',
+ 'security_invoker=true',
+ 'security_invoker=yes',
+ 'security_invoker=on'
+ ]
+ )
+ and not pg_class_auth_users.relrowsecurity
+ )
+ )
group by
c.relname, c.oid)
union all
@@ -151,12 +198,12 @@ with policies as (
qual,
with_check
from
- pg_policy pa
- join pg_class pc
+ pg_catalog.pg_policy pa
+ join pg_catalog.pg_class pc
on pa.polrelid = pc.oid
- join pg_namespace nsp
+ join pg_catalog.pg_namespace nsp
on pc.relnamespace = nsp.oid
- join pg_policies pb
+ join pg_catalog.pg_policies pb
on pc.relname = pb.tablename
and nsp.nspname = pb.schemaname
and pa.polname = pb.policyname
@@ -182,6 +229,9 @@ from
policies
where
is_rls_active
+ and schema_::text not in (
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
+ )
and (
(
-- Example: auth.uid()
@@ -210,7 +260,7 @@ select
pgc.relname
) as detail,
'https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key' as remediation,
- jsonb_build_object(
+ jsonb_build_object(
'schema', pgns.nspname,
'name', pgc.relname,
'type', 'table'
@@ -221,15 +271,15 @@ select
pgc.relname
) as cache_key
from
- pg_class pgc
- join pg_namespace pgns
+ pg_catalog.pg_class pgc
+ join pg_catalog.pg_namespace pgns
on pgns.oid = pgc.relnamespace
- left join pg_index pgi
+ left join pg_catalog.pg_index pgi
on pgi.indrelid = pgc.oid
where
pgc.relkind = 'r' -- regular tables
and pgns.nspname not in (
- 'pg_catalog', 'information_schema', 'auth', 'storage', 'vault', 'pgsodium'
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
)
group by
pgc.oid,
@@ -272,7 +322,7 @@ where
and not pi.indisunique
and not pi.indisprimary
and psui.schemaname not in (
- 'pg_catalog', 'information_schema', 'auth', 'storage', 'vault', 'pgsodium'
+ 'pg_catalog', 'information_schema', 'auth', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
))
union all
(
@@ -326,7 +376,7 @@ from
where
c.relkind = 'r' -- regular tables
and n.nspname not in (
- 'pg_catalog', 'information_schema', 'auth', 'storage', 'vault', 'pgsodium'
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
)
and r.rolname not like 'pg_%'
and r.rolname not like 'supabase%admin'
@@ -337,16 +387,256 @@ group by
r.rolname,
act.cmd
having
- count(1) > 1)`.trim()
+ count(1) > 1)
+union all
+(
+select
+ 'policy_exists_rls_disabled' as name,
+ 'INFO' as level,
+ 'EXTERNAL' as facing,
+ 'Detects cases where row level security (RLS) policies have been created, but RLS has not been enabled for the underlying table.' as description,
+ 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,
+ format(
+ 'policy_exists_rls_disabled_%s_%s',
+ n.nspname,
+ c.relname
+ ) as cache_key
+from
+ pg_catalog.pg_policy p
+ join pg_catalog.pg_class c
+ on p.polrelid = c.oid
+ join pg_catalog.pg_namespace n
+ on c.relnamespace = n.oid
+where
+ c.relkind = 'r' -- regular tables
+ and n.nspname not in (
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
+ )
+ -- RLS is disabled
+ and not c.relrowsecurity
+group by
+ n.nspname,
+ c.relname)
+union all
+(
+select
+ 'rls_enabled_no_policy' as name,
+ 'INFO' as level,
+ 'EXTERNAL' as facing,
+ 'Detects cases where row level security (RLS) has been enabled on a table but no RLS policies have been created.' as description,
+ format(
+ 'Table \`%s.%s\` has RLS enabled, but no policies exist',
+ n.nspname,
+ c.relname
+ ) as detail,
+ 'https://supabase.com/docs/guides/database/database-linter?lint=0008_rls_enabled_no_policy' as remediation,
+ jsonb_build_object(
+ 'schema', n.nspname,
+ 'name', c.relname,
+ 'type', 'table'
+ ) as metadata,
+ format(
+ 'rls_enabled_no_policy_%s_%s',
+ n.nspname,
+ c.relname
+ ) as cache_key
+from
+ pg_catalog.pg_class c
+ left join pg_catalog.pg_policy p
+ on p.polrelid = c.oid
+ join pg_catalog.pg_namespace n
+ on c.relnamespace = n.oid
+where
+ c.relkind = 'r' -- regular tables
+ and n.nspname not in (
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
+ )
+ -- RLS is enabled
+ and c.relrowsecurity
+ and p.polname is null
+group by
+ n.nspname,
+ c.relname)
+union all
+(
+select
+ 'duplicate_index' as name,
+ 'WARN' as level,
+ 'EXTERNAL' as facing,
+ 'Detects cases where two ore more identical indexes exist.' as description,
+ format(
+ 'Table \`%s.%s\` has identical indexes %s. Drop all except one of them',
+ n.nspname,
+ c.relname,
+ array_agg(pi.indexname order by pi.indexname)
+ ) as detail,
+ 'https://supabase.com/docs/guides/database/database-linter?lint=0009_duplicate_index' as remediation,
+ jsonb_build_object(
+ 'schema', n.nspname,
+ 'name', c.relname,
+ 'type', case
+ when c.relkind = 'r' then 'table'
+ when c.relkind = 'm' then 'materialized view'
+ else 'ERROR'
+ end,
+ 'indexes', array_agg(pi.indexname order by pi.indexname)
+ ) as metadata,
+ format(
+ 'duplicate_index_%s_%s_%s',
+ n.nspname,
+ c.relname,
+ array_agg(pi.indexname order by pi.indexname)
+ ) as cache_key
+from
+ pg_catalog.pg_indexes pi
+ join pg_catalog.pg_namespace n
+ on n.nspname = pi.schemaname
+ join pg_catalog.pg_class c
+ on pi.tablename = c.relname
+ and n.oid = c.relnamespace
+where
+ c.relkind in ('r', 'm') -- tables and materialized views
+ and n.nspname not in (
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
+ )
+group by
+ n.nspname,
+ c.relkind,
+ c.relname,
+ replace(pi.indexdef, pi.indexname, '')
+having
+ count(*) > 1)
+union all
+(
+select
+ 'security_definer_view' as name,
+ 'WARN' as level,
+ 'EXTERNAL' as facing,
+ 'Detects views that are SECURITY DEFINER meaning that they ignore row level security (RLS) policies.' as description,
+ format(
+ 'View \`%s.%s\` is SECURITY DEFINER',
+ n.nspname,
+ c.relname
+ ) as detail,
+ 'https://supabase.com/docs/guides/database/database-linter?lint=0010_security_definer_view' as remediation,
+ jsonb_build_object(
+ 'schema', n.nspname,
+ 'name', c.relname,
+ 'type', 'view'
+ ) as metadata,
+ format(
+ 'security_definer_view_%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
+where
+ c.relkind = 'v'
+ and n.nspname = 'public'
+ and not (
+ lower(coalesce(c.reloptions::text,'{}'))::text[]
+ && array[
+ 'security_invoker=1',
+ 'security_invoker=true',
+ 'security_invoker=yes',
+ 'security_invoker=on'
+ ]
+ ))
+union all
+(
+select
+ 'function_search_path_mutable' as name,
+ 'WARN' as level,
+ 'EXTERNAL' as facing,
+ 'Detects functions with a mutable search_path parameter which could fail to execute successfully for some roles.' as description,
+ format(
+ 'Function \`%s.%s\` has a role mutable search_path',
+ n.nspname,
+ p.proname
+ ) as detail,
+ 'https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable' as remediation,
+ jsonb_build_object(
+ 'schema', n.nspname,
+ 'name', p.proname,
+ 'type', 'function'
+ ) as metadata,
+ format(
+ 'function_search_path_mutable_%s_%s_%s',
+ n.nspname,
+ p.proname,
+ md5(p.prosrc) -- required when function is polymorphic
+ ) as cache_key
+from
+ pg_catalog.pg_proc p
+ join pg_catalog.pg_namespace n
+ on p.pronamespace = n.oid
+where
+ n.nspname not in (
+ 'pg_catalog', 'information_schema', 'auth', 'extensions', 'graphql', 'graphql_public', 'net', 'pgsodium', 'storage', 'supabase_functions', 'vault'
+ )
+ -- Search path not set to ''
+ and not coalesce(p.proconfig, '{}') && array['search_path=""'])
+union all
+(
+select
+ 'rls_disabled_in_public' as name,
+ 'ERROR' as level,
+ 'EXTERNAL' as facing,
+ 'Detects cases where row level security (RLS) has not been enabled on a table in the \`public\` schema.' as description,
+ format(
+ 'Table \`%s.%s\` is public, but RLS has not been enabled.',
+ n.nspname,
+ c.relname
+ ) as detail,
+ 'https://supabase.com/docs/guides/database/database-linter?lint=0013_rls_disabled_in_public' as remediation,
+ jsonb_build_object(
+ 'schema', n.nspname,
+ 'name', c.relname,
+ 'type', 'table'
+ ) as metadata,
+ format(
+ 'rls_disabled_in_public_%s_%s',
+ n.nspname,
+ c.relname
+ ) as cache_key
+from
+ pg_catalog.pg_class c
+ join pg_catalog.pg_namespace n
+ on c.relnamespace = n.oid
+where
+ c.relkind = 'r' -- regular tables
+ and n.nspname = 'public'
+ -- RLS is disabled
+ and not c.relrowsecurity)`.trim()
// Array of all lint rules we handle right now.
export const LINT_TYPES = [
'unindexed_foreign_keys',
'auth_users_exposed',
+ 'auth_rls_initplan',
'no_primary_key',
'unused_index',
'multiple_permissive_policies',
- 'auth_rls_initplan',
+ 'policy_exists_rls_disabled',
+ 'rls_enabled_no_policy',
+ 'duplicate_index',
+ 'security_definer_view',
+ 'function_search_path_mutable',
+ 'rls_disabled_in_public',
] as const
export type LINT_TYPES = (typeof LINT_TYPES)[number]
diff --git a/apps/studio/pages/project/[ref]/database/linter.tsx b/apps/studio/pages/project/[ref]/database/linter.tsx
index 31f48daa024..039790ad105 100644
--- a/apps/studio/pages/project/[ref]/database/linter.tsx
+++ b/apps/studio/pages/project/[ref]/database/linter.tsx
@@ -1,25 +1,18 @@
-import { partition, sortBy } from 'lodash'
+import { sortBy } from 'lodash'
import { Check, ExternalLink, Loader } from 'lucide-react'
import { useMemo, useState } from 'react'
-import { AccordionTrigger } from '@ui/components/shadcn/ui/accordion'
+import { getHumanReadableTitle } from 'components/interfaces/Reports/ReportLints.utils'
import ReportLintsTableRow from 'components/interfaces/Reports/ReportLintsTableRow'
import { DatabaseLayout } from 'components/layouts'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import Table from 'components/to-be-cleaned/Table'
-import { FormHeader } from 'components/ui/Forms'
-import { useProjectLintsQuery } from 'data/lint/lint-query'
-import { useLocalStorageQuery, useSelectedProject } from 'hooks'
-import { LOCAL_STORAGE_KEYS } from 'lib/constants'
-import type { NextPageWithLayout } from 'types'
-import {
- AccordionContent_Shadcn_,
- AccordionItem_Shadcn_,
- Accordion_Shadcn_,
- Button,
- LoadingLine,
-} from 'ui'
import { FilterPopover } from 'components/ui/FilterPopover'
+import { FormHeader } from 'components/ui/Forms'
+import { LINT_TYPES, useProjectLintsQuery } from 'data/lint/lint-query'
+import { useSelectedProject } from 'hooks'
+import type { NextPageWithLayout } from 'types'
+import { Button, LoadingLine } from 'ui'
const ProjectLints: NextPageWithLayout = () => {
const project = useSelectedProject()
@@ -27,10 +20,10 @@ const ProjectLints: NextPageWithLayout = () => {
levels: [] as string[],
types: [] as string[],
})
- const [lintIgnoreList] = useLocalStorageQuery(
- LOCAL_STORAGE_KEYS.PROJECT_LINT_IGNORE_LIST,
- []
- )
+ // const [lintIgnoreList] = useLocalStorageQuery(
+ // LOCAL_STORAGE_KEYS.PROJECT_LINT_IGNORE_LIST,
+ // []
+ // )
const { data, isLoading, isRefetching, refetch } = useProjectLintsQuery({
projectRef: project?.ref,
@@ -45,9 +38,10 @@ const ProjectLints: NextPageWithLayout = () => {
return 3
})
- const [ignoredLints, activeLints] = partition(lints, (lint) =>
- lintIgnoreList.includes(lint.cache_key)
- )
+ const activeLints = lints
+ // const [ignoredLints, activeLints] = partition(lints, (lint) =>
+ // lintIgnoreList.includes(lint.cache_key)
+ // )
const filteredLints = useMemo(() => {
return activeLints
.filter((x) => (filters.levels.length > 0 ? filters.levels.includes(x.level) : x))
@@ -57,6 +51,16 @@ const ProjectLints: NextPageWithLayout = () => {
const warnLintsCount = activeLints.filter((x) => x.level === 'WARN').length
const errorLintsCount = activeLints.filter((x) => x.level === 'ERROR').length
+ const filterOptions = useMemo(() => {
+ // only show filters for lint types which are present in the results and not ignored
+ return LINT_TYPES.filter((type) => activeLints.some((lint) => lint.name === type)).map(
+ (type) => ({
+ name: getHumanReadableTitle(type),
+ value: type,
+ })
+ )
+ }, [activeLints])
+
return (
@@ -86,14 +90,7 @@ const ProjectLints: NextPageWithLayout = () => {
/>
{
/>
- {ignoredLints.length > 0 && (
+ {/* {ignoredLints.length > 0 && (
@@ -219,7 +216,7 @@ const ProjectLints: NextPageWithLayout = () => {
- )}
+ )} */}
)