import { ChevronRight, ChevronUp } from 'lucide-react' import { useMemo, useState } from 'react' import { Button, Collapsible, CollapsibleContent, CollapsibleTrigger } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { type ExposedEntity } from './DataApiEnableSwitch.utils' interface UnsafeEntitiesConfirmModalProps { visible: boolean loading: boolean unsafeEntities: Array onCancel: () => void onConfirm: () => void } const ENTITY_TYPE_META: Record< ExposedEntity['type'], { heading: string; recommendation: string; docsUrl: string } > = { table: { heading: 'Tables without Row Level Security', recommendation: 'Enable RLS on these tables to control access per-row.', docsUrl: 'https://supabase.com/docs/guides/database/database-linter?lint=0013_rls_disabled_in_public', }, 'foreign table': { heading: 'Foreign tables', recommendation: 'Foreign tables do not support RLS. Revoke access from the anon and authenticated roles.', docsUrl: 'https://supabase.com/docs/guides/database/database-linter?lint=0017_foreign_table_in_api', }, 'materialized view': { heading: 'Materialized views', recommendation: 'Materialized views do not support RLS. Revoke access from the anon and authenticated roles.', docsUrl: 'https://supabase.com/docs/guides/database/database-linter?lint=0016_materialized_view_in_api', }, view: { heading: 'Views without SECURITY INVOKER', recommendation: 'These views run with the permissions of the view creator, not the querying user. Set SECURITY INVOKER to enforce caller permissions.', docsUrl: 'https://supabase.com/docs/guides/database/database-linter?lint=0010_security_definer_view', }, } const ENTITY_TYPE_ORDER: Array = [ 'table', 'foreign table', 'materialized view', 'view', ] const COLLAPSE_THRESHOLD = 3 export const UnsafeEntitiesConfirmModal = ({ visible, loading, unsafeEntities, onCancel, onConfirm, }: UnsafeEntitiesConfirmModalProps) => { const groupedEntities = useMemo(() => { const groups = new Map>() for (const entity of unsafeEntities) { const group = groups.get(entity.type) if (group) { group.push(entity) } else { groups.set(entity.type, [entity]) } } return ENTITY_TYPE_ORDER.filter((type) => groups.has(type)).map((type) => ({ type, ...ENTITY_TYPE_META[type], entities: groups.get(type) ?? [], })) }, [unsafeEntities]) return (

The following objects will be publicly accessible through the Data API and are insecure.

{groupedEntities.map(({ type, heading, recommendation, docsUrl, entities }) => (

{heading}

{recommendation}{' '} Learn more

))}
) } const EntityListItem = ({ entity }: { entity: ExposedEntity }) => (
  • {entity.schema}.{entity.name}
  • ) const EntityList = ({ entities }: { entities: Array }) => { const [open, setOpen] = useState(false) const shouldCollapse = entities.length > COLLAPSE_THRESHOLD const visibleEntities = entities.slice(0, COLLAPSE_THRESHOLD) const hiddenEntities = entities.slice(COLLAPSE_THRESHOLD) if (!shouldCollapse) { return (
      {entities.map((entity) => ( ))}
    ) } return (
      {visibleEntities.map((entity) => ( ))}
      {hiddenEntities.map((entity) => ( ))}
    ) }