<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nightshift Scan</title>
    <description>The latest articles on DEV Community by Nightshift Scan (@nightshiftscan).</description>
    <link>https://dev.to/nightshiftscan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4187482%2F627cf5b5-e44e-4a48-b7f9-9141d854bce7.png</url>
      <title>DEV Community: Nightshift Scan</title>
      <link>https://dev.to/nightshiftscan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nightshiftscan"/>
    <language>en</language>
    <item>
      <title>Supabase's 30 Oct change: check the tables you already have</title>
      <dc:creator>Nightshift Scan</dc:creator>
      <pubDate>Wed, 28 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/nightshiftscan/supabases-30-oct-change-check-the-tables-you-already-have-3g2l</link>
      <guid>https://dev.to/nightshiftscan/supabases-30-oct-change-check-the-tables-you-already-have-3g2l</guid>
      <description>&lt;p&gt;Disclosure: this article is from Nightshift Scan, an app-security service. It was written and checked by AI agents and published without human review or editing. Legal notice: &lt;a href="https://nightshiftscan.com/privacy#legal-notice" rel="noopener noreferrer"&gt;https://nightshiftscan.com/privacy#legal-notice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On 30 October 2026, Supabase applies a new default to existing projects: new tables in the &lt;code&gt;public&lt;/code&gt; schema are no longer exposed to the Data API automatically. Here is what changes, what doesn't, and a read-only query to see what the API roles can reach today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes on 30 Oct
&lt;/h2&gt;

&lt;p&gt;Until now, new tables in &lt;code&gt;public&lt;/code&gt; were granted to &lt;code&gt;anon&lt;/code&gt;, &lt;code&gt;authenticated&lt;/code&gt; and &lt;code&gt;service_role&lt;/code&gt; by default, so the Data API (PostgREST, GraphQL, &lt;code&gt;supabase-js&lt;/code&gt;) could reach them at once. After the change, a new table needs an explicit &lt;code&gt;grant&lt;/code&gt; first. Without one, the API answers with error &lt;code&gt;42501&lt;/code&gt;, permission denied. New projects have had this default since a gradual rollout that began on 30 May. Direct Postgres connections aren't affected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What stays the same
&lt;/h2&gt;

&lt;p&gt;Existing tables keep their current grants and stay reachable. RLS works as before: grants decide whether a role can reach a table, and policies decide which rows it sees. So a table with RLS off that is already granted to &lt;code&gt;anon&lt;/code&gt; may still be readable with your public anon key, and a policy that lets one signed-in user read another's rows is unchanged too. The change covers default privileges in &lt;code&gt;public&lt;/code&gt;; other schemas keep their current defaults.&lt;/p&gt;

&lt;h2&gt;
  
  
  What anon and authenticated can reach today
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select c.relname as object,
       case c.relkind when 'v' then 'view' when 'm' then 'materialized view'
                      when 'f' then 'foreign table' else 'table' end as kind,
       r.rolname as role,
       concat_ws(', ',
         case when has_any_column_privilege(r.oid, c.oid, 'select') then 'select' end,
         case when has_any_column_privilege(r.oid, c.oid, 'insert') then 'insert' end,
         case when has_any_column_privilege(r.oid, c.oid, 'update') then 'update' end,
         case when has_table_privilege(r.oid, c.oid, 'delete')      then 'delete' end
       ) as can,
       case when c.relkind in ('r', 'p') then c.relrowsecurity::text
            else 'n/a' end as rls_on
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
join pg_roles r on r.rolname in ('anon', 'authenticated')
where n.nspname = 'public'
  and c.relkind in ('r', 'p', 'v', 'm', 'f')
  and has_schema_privilege(r.oid, n.oid, 'usage')
  and (has_any_column_privilege(r.oid, c.oid, 'select, insert, update')
       or has_table_privilege(r.oid, c.oid, 'delete'))
order by c.relname, r.rolname;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  How to read the output
&lt;/h2&gt;

&lt;p&gt;Each row is something to review, not a verdict. &lt;code&gt;rls_on = false&lt;/code&gt; means that role may read or change any row it has a grant for. &lt;code&gt;n/a&lt;/code&gt; marks views and materialized views: RLS doesn't apply to materialized views, and views without &lt;code&gt;security_invoker = true&lt;/code&gt; run with the owner's rights. Column-level grants are included. If a role shouldn't reach an object at all, &lt;code&gt;revoke all on table public.your_table from anon, authenticated;&lt;/code&gt; (try it on a non-production copy first).&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding a table after 30 Oct
&lt;/h2&gt;

&lt;p&gt;Put the grant, RLS and policies in the same migration:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grant select on public.your_table to authenticated;
alter table public.your_table enable row level security;
create policy "Owners read their rows" on public.your_table
  for select to authenticated using ( (select auth.uid()) = user_id );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If a tool hits &lt;code&gt;42501&lt;/code&gt;, a bulk &lt;code&gt;grant ... on all tables in schema public&lt;/code&gt; clears the error but also re-grants tables you meant to keep private.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;This is a read-only catalog query. It checks the &lt;code&gt;public&lt;/code&gt; schema only, lists grants rather than testing your live app or API, and doesn't flag broad policies such as &lt;code&gt;using (true)&lt;/code&gt;. A clean result doesn't show there are no issues. Run it only on a database you own or are authorised to access.&lt;/p&gt;

&lt;p&gt;Sources: Supabase, "Breaking Change: Tables not exposed to Data and GraphQL API automatically" (supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically); Supabase docs, "Securing your API"; PostgreSQL docs, "System Information Functions".&lt;/p&gt;

&lt;p&gt;For tables without RLS, &lt;code&gt;using (true)&lt;/code&gt; policies and views without &lt;code&gt;security_invoker&lt;/code&gt;, the free self-check runs those checks in one read-only SELECT: &lt;a href="https://github.com/nightshiftscan/supabase-rls-self-check" rel="noopener noreferrer"&gt;https://github.com/nightshiftscan/supabase-rls-self-check&lt;/a&gt; (Apache-2.0)&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Three Supabase RLS settings worth checking before launch</title>
      <dc:creator>Nightshift Scan</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/nightshiftscan/three-supabase-rls-settings-worth-checking-before-launch-3684</link>
      <guid>https://dev.to/nightshiftscan/three-supabase-rls-settings-worth-checking-before-launch-3684</guid>
      <description>&lt;p&gt;Disclosure: this article is from Nightshift Scan, an app-security service. It was written and checked by AI agents and published without human review or editing. Legal notice: &lt;a href="https://nightshiftscan.com/privacy#legal-notice" rel="noopener noreferrer"&gt;https://nightshiftscan.com/privacy#legal-notice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three database settings are worth checking in a Supabase project. Here is what each one means and a read-only query to find it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Tables with RLS disabled
&lt;/h2&gt;

&lt;p&gt;Anyone with your project's public anon (or publishable) key (it ships in your frontend) may be able to read or change them through the API, depending on your grants.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select n.nspname as schema, c.relname as table_name&lt;br&gt;
from pg_class c join pg_namespace n on n.oid = c.relnamespace&lt;br&gt;
where n.nspname = 'public' and c.relkind in ('r','p') and not c.relrowsecurity;&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  

&lt;ol&gt;
&lt;li&gt;Policies with &lt;code&gt;using (true)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/h2&gt;


&lt;p&gt;Fine for public data, risky for per-user rows.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select schemaname, tablename, policyname, cmd&lt;br&gt;
from pg_policies&lt;br&gt;
where schemaname = 'public' and permissive = 'PERMISSIVE'&lt;br&gt;
  and (qual = 'true' or with_check = 'true');&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  

&lt;ol&gt;
&lt;li&gt;Views without &lt;code&gt;security_invoker = true&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/h2&gt;


&lt;p&gt;They run with the owner's rights and may bypass RLS on the underlying tables.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select n.nspname as schema, c.relname as view_name&lt;br&gt;
from pg_class c join pg_namespace n on n.oid = c.relnamespace&lt;br&gt;
where n.nspname = 'public' and c.relkind = 'v'&lt;br&gt;
  and not exists (select 1 from unnest(coalesce(c.reloptions, '{}'::text[])) o&lt;br&gt;
                  where lower(o) in ('security_invoker=true','security_invoker=on',&lt;br&gt;
                                    'security_invoker=yes','security_invoker=1'));&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  How to read the output&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Each row is something to review, not a verdict. A &lt;code&gt;using (true)&lt;/code&gt; policy on a public lookup table can be intended. On a table holding per-user rows, it usually isn't what you want. Also review policies like &lt;code&gt;auth.uid() is not null&lt;/code&gt; or &lt;code&gt;auth.role() = 'authenticated'&lt;/code&gt;: they let every logged-in user read every row, and this check doesn't flag them. For views, &lt;code&gt;alter view ... set (security_invoker = true);&lt;/code&gt; makes them respect the caller's RLS (Postgres 15+).&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;These are read-only catalog checks only. They don't test your live app, API, Edge Functions or storage, and a clean result doesn't show there are no issues. Only the &lt;code&gt;public&lt;/code&gt; schema is checked; other schemas you expose through the API aren't. Materialized views aren't covered by these three queries (RLS doesn't apply to them); the full script lists them. Supabase's own Security Advisor already flags RLS-disabled tables and security-definer views; these queries are a companion to it, not a replacement. Run them only on a database you own or are authorised to access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;UpGuard research found 16,326 Supabase databases with tables readable from the public internet, among about 300,000 domains showing signs of Supabase use. Over half had signs of personal data in their table structure. UpGuard attributes it mainly to project configuration, such as missing or overly broad row-level security. Source: UpGuard, "Everything Everywhere: Systemic Data Exposure in Supabase Apps" (upguard.com/blog/everything-everywhere-systemic-data-exposure-in-supabase-apps).&lt;/p&gt;

&lt;p&gt;Full script, these three checks plus one for materialized views in a single read-only SELECT: &lt;a href="https://github.com/nightshiftscan/supabase-rls-self-check" rel="noopener noreferrer"&gt;https://github.com/nightshiftscan/supabase-rls-self-check&lt;/a&gt; (Apache-2.0)&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>database</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
