Skip to content

Conversation

@riderx
Copy link
Contributor

@riderx riderx commented Dec 30, 2025

Summary

Adds two new security lints inspired by external security scanning tools to detect common Supabase misconfigurations that can lead to data exposure.

New Lints

0023_sensitive_columns_exposed (ERROR)

Detects tables exposed via the API that contain columns with potentially sensitive data (PII, credentials, financial info) without RLS protection.

Sensitive patterns detected:

  • Credentials: password, secret, api_key, token, jwt, otp, 2fa_secret
  • Personal IDs: ssn, driver_license, passport_number, national_id, tax_id
  • Financial: credit_card, cvv, bank_account, iban, routing_number
  • Medical: health_record, diagnosis, patient_id
  • Digital Keys: ssh_key, pgp_key, certificate
  • Biometric: fingerprint, biometric

0024_permissive_rls_policy (WARN)

Detects RLS policies that use overly permissive expressions, which effectively bypass row-level security while giving a false sense of protection.

Patterns detected:

  • USING (true) - allows reading all rows
  • WITH CHECK (true) - allows writing any row
  • USING (1=1) or similar tautologies
  • Missing USING/WITH CHECK clauses on permissive policies

Key features:

  • Only flags permissive policies (restrictive policies with true are less dangerous)
  • Only flags policies applying to anon, authenticated, or public roles
  • Does NOT flag policies for custom roles

Why This Matters

These are common security misconfigurations:

  1. Developers create tables with sensitive columns and forget to enable RLS
  2. Developers enable RLS but create placeholder USING (true) policies during development and forget to update them

Both scenarios expose data while potentially giving a false sense of security.

Changes

  • Added lints/0023_sensitive_columns_exposed.sql
  • Added lints/0024_permissive_rls_policy.sql
  • Added documentation for both lints
  • Added tests for both lints
  • Updated bin/installcheck and splinter.sql

Test Results

All 24 tests passed.

This commit introduces a new lint that identifies tables exposed via the Supabase Data APIs containing columns with potentially sensitive data (e.g., passwords, SSNs, credit card numbers) without Row Level Security (RLS) enabled. It includes a detailed SQL view and documentation outlining the rationale, patterns detected, and recommended resolutions for mitigating security risks.
This commit introduces a new lint that identifies tables exposed via the Supabase Data APIs containing columns with potentially sensitive data (e.g., passwords, SSNs, credit card numbers) without Row Level Security (RLS) enabled. It includes a detailed SQL view and documentation outlining the rationale, patterns detected, and recommended resolutions for mitigating security risks.
@riderx
Copy link
Contributor Author

riderx commented Dec 30, 2025

Copy link
Collaborator

@olirice olirice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few change requests. Overall looks great. Thanks for the contribution and nice work on the test cases

Comment on lines 73 to 76
lower(a.attname) like '%' || sp.pattern || '%'
or lower(a.attname) = sp.pattern
-- Also check for common variations with underscores/hyphens removed
or replace(replace(lower(a.attname), '_', ''), '-', '') like '%' || replace(sp.pattern, '_', '') || '%'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wildcards are going to result in many false positives. Lets stick to

replace(lower(a.attname), '-', '_') = sp.pattern

since that will catch - and _ variants only

@@ -0,0 +1,124 @@
create view lint."0024_permissive_rls_policy" as

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets rename this to "rls_policy_always_true" since permissive has a specific meaning in RLS

Comment on lines 43 to 56
case when (
-- Literal true
lower(trim(coalesce(qual, ''))) = 'true'
-- 1=1 or similar tautologies
or lower(trim(coalesce(qual, ''))) ~ '^[\s\(]*1\s*=\s*1[\s\)]*$'
or lower(trim(coalesce(qual, ''))) ~ '^[\s\(]*''[^'']*''\s*=\s*''[^'']*''[\s\)]*$'
-- Empty or null qual on permissive policy means allow all for SELECT
or (qual is null and is_permissive and command in ('SELECT', 'ALL'))
) then true else false end as has_permissive_using,
-- Check for always-true WITH CHECK clause patterns
case when (
lower(trim(coalesce(with_check, ''))) = 'true'
or lower(trim(coalesce(with_check, ''))) ~ '^[\s\(]*1\s*=\s*1[\s\)]*$'
or lower(trim(coalesce(with_check, ''))) ~ '^[\s\(]*''[^'']*''\s*=\s*''[^'']*''[\s\)]*$'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the two most common variants are (true) and (1=1). Lets check for those specifically and get rid of the regex

pa.polroles as role_oids,
(select array_agg(r::regrole::text) from unnest(pa.polroles) as x(r)) as roles,
case pa.polcmd
when 'r' then 'SELECT'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should continue to allow (true) for SELECT. Its often intentional and even documented in a few places. In contrast, UPDATE/DELETE should never be wide open

'license_key', 'activation_key',
-- Biometric Data
'fingerprint', 'biometric', 'facial_recognition'
]) as pattern
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass, diagnosis, fingerprint, biometric, and facial_recognition don't seem high enough signal-to-noise compared with the other. lets remove those

@riderx
Copy link
Contributor Author

riderx commented Jan 4, 2026

@olirice thanks for the feedback this is fixed

@awaseem awaseem merged commit 019708a into supabase:main Jan 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants