v0.1.98 — 2 Oct 2026
This release contains security fixes. Upgrade is recommended for every deployment. Two of them change behavior:
- The anon key no longer reaches any data. A client that calls PostgREST
directly with the anon key now gets
401or403. The Studio and the API are not affected. - Administrator access comes only from grants at the root. A user whose administrator access came only from a grant at a tenant loses it. See Administrator access granted at a tenant applied everywhere.
Security — the anon key was able to disable row-level security and read any table
Who is affected: Every deployment. The anon key is public, so no account was needed.
Every browser gets the anon key. Supabase gives the anon role EXECUTE on each
new database function, and PostgreSQL gives it to PUBLIC. The DaaS migrations
did not revoke these grants. With the anon key only, a caller was able to:
- call
remove_rls_from_collectionon any table. The function drops all policies of the table and disables row-level security. Ondaas_users, that opened every row to the anon key. - call
execute_aggregateon any table. The function skips row-level security, and agroup_byon a column returned all distinct values of that column, for example every email address indaas_users. - call
change_table_owner,apply_rls_to_collection, andlist_rls_policies, which change table ownership, change policies, or show policy definitions. - read, change, and delete the rows of
daas_collections, the collection metadata.
Now only the service role can execute these functions, and the application calls
them only from the server. Signed-in users can read daas_collections, and only
administrators can change it.
If your deployment allowed self sign-up before this release, check your
database logs for calls to /rpc/remove_rls_from_collection and
/rpc/execute_aggregate.
Security — the anon key was able to read through public grants
Who is affected: Deployments with a public policy grant, that is, a policy attached with no role and no user.
Many policies on system tables applied to every database role, anon included.
When a public grant exists, the permission functions that those policies call
answer “yes” for a caller with no user. The anon key was therefore able to read
rows through a public grant, for example from daas_files, daas_settings, or
daas_activity.
DaaS serves no data to a caller that is not signed in. A public grant means “all
signed-in users”, and that does not change. Every policy now applies to
signed-in users only. The anon role can execute no function and holds no
privileges on the tables, views, and sequences of the public schema, and new
tables and views do not give it any.
Security — aggregates ignored tenant scope, field permissions, and concealed fields
Who is affected: Deployments that use tenant scopes, field-level read permissions, or concealed fields such as static tokens.
GET /api/items/:collection?aggregate=… and the aggregate action of the MCP
items tool checked the read permission for the collection and applied the
item-level permission filter. They missed three checks:
| Request | Before | Now |
|---|---|---|
| Count at tenant A | Counted the rows of every tenant | Counts tenant A only (and its children with downward inheritance) |
groupBy or max on a field the user cannot read | Returned the values | 403 |
groupBy on a concealed field, such as a static token | Returned the values unmasked, also to administrators | 403 for every user |
Aggregates now apply the same scope filter as list reads, including
missing_uri_mode = reject.
Security — administrator access granted at a tenant applied everywhere
Who is affected: Deployments that assign the Administrator role, or an admin policy, to a user or role at a tenant.
The functions that decide administrator and delegate access ignored the resource URI of a grant. An admin policy that reached a user through a grant at one tenant made that user an administrator at the root and in every other tenant, and the application then skipped the tenant check for that user. Two ordinary Studio actions caused it:
- the Administrator role assigned to a user at a tenant;
- an admin policy granted to a role or a user at a tenant.
Administrator access now needs every link of the grant to be at the root.
Delegate access follows the same rule. The users list and GET /api/roles/me
use the same rule.
A user whose administrator access came only from a grant at a tenant loses it. To let a user manage one tenant, grant a non-admin policy, with permissions on the collections of that tenant, at that tenant.
Security — importing a file from a URL reached private addresses
Who is affected: Every deployment where users can create files.
POST /api/files/import fetched any http or https URL and followed
redirects. Any caller who can create a file was able to make the server read a
private address, such as the loopback interface, a private network, or the cloud
metadata address 169.254.169.254. The answer was stored as a file that the same
caller can download.
The import now refuses loopback, private, link-local, carrier-grade NAT,
multicast, and reserved addresses with 400 IMPORT_URL_REFUSED. The address is
judged at the moment the socket connects, so DNS rebinding cannot swap it.
Redirects are followed by hand, at most five, and every hop is judged again.
If your deployment imports files from an internal host on purpose, set
STORAGE_IMPORT_ALLOWED_NETWORKS to the addresses or CIDR prefixes to allow:
STORAGE_IMPORT_ALLOWED_NETWORKS=10.20.0.0/16,192.168.5.10The variable is not listed in docker-compose.yml. Add it to the
environment list. See environment variables.
Security — database functions have a fixed search path
Who is affected: Every deployment. No action is needed.
25 database functions had no search_path setting, and eight of them run with
the rights of their owner. The Supabase Security Advisor showed them as
“Function Search Path Mutable”. Every function in the public schema now has
search_path = public, pg_temp. The advisor warnings clear after the upgrade.
Fixed — a new column or table answered PGRST204 right after it was created
Who is affected: Anyone who creates a field, a collection, or a relation and uses it in the next request.
The database commits a schema change at once, but PostgREST rebuilds its schema
cache a moment later. A request that named the new column in that window failed
with PGRST204, “Could not find the column … in the schema cache”, although the
column existed. The API now waits until PostgREST can serve the change, for at
most 3 seconds, before it answers. On a six-table schema, a new table became
readable in 120 to 270 ms.
Fixed — a collection related to itself through a junction table can be read
Who is affected: Many-to-many relations where both foreign keys of the junction table point to the same collection.
Such a read failed with “Could not embed because more than one relationship was found”. Embedded selects now name the foreign key column in both directions, for every many-to-many relation, and a filter on the alias no longer adds a second embed.
Fixed — a field created through MCP now gets its relation metadata
Who is affected: Anyone who creates many-to-one fields through the MCP fields tool.
The tool created the column with its own SQL, so a many-to-one field got no
foreign key and no relation metadata. Lookups that read the schema did not find
the field, and workflow_instance stayed empty even for an item whose workflow
was progressing correctly. The tool now uses the same service as the Studio. It
also reuses a foreign key that is already on the column, reports a foreign key
that failed instead of reporting success, and treats meta.required as
NOT NULL.
Fixed — other
- Field types are applied on read. The read transforms never ran, so a
csvfield returned its stored comma string, such as"a,b", instead of["a", "b"].cast-jsonandcast-booleanhad the same problem. - A
csvfield stores a comma string. An array is now joined for a field on a plain text column, and kept for a field on an array column. Newcsvfields get thecast-csvtransform automatically. - The
input-tagsandinput-mapinterfaces use the correct default. Interfaces saved under those names fell through to the default for their type.
Changed
- Next.js 16.3.6. Next.js and
eslint-config-nextare upgraded to 16.3.6. - Buildpad UI components upgraded to registry 2.6.0. Relation dropdowns and
the form field interface pick up their upstream fixes. A new
BUILDPAD_UI_UPGRADE_GUIDE.mddescribes how to run these upgrades for this project.
Internal
A new test suite talks to the database with the anon key only and fails if the
anon role can execute any function or read any table. It reads status codes
and row counts only. It also covers aggregates for tenant scope, concealed
fields, and field-level permissions.