Skip to content

fix(db): relocate postgis to the extensions schema to clear spatial_ref_sys lint#112

Merged
erichare merged 2 commits into
mainfrom
fix/relocate-postgis-extensions-schema
Jun 10, 2026
Merged

fix(db): relocate postgis to the extensions schema to clear spatial_ref_sys lint#112
erichare merged 2 commits into
mainfrom
fix/relocate-postgis-extensions-schema

Conversation

@erichare

Copy link
Copy Markdown
Owner

Summary

Resolves the last two Supabase security advisor findings on project qijajhckwxsclvyzeaez: rls_disabled_in_public (0013) on public.spatial_ref_sys and extension_in_public (0014) on postgis — the documented known limitation left open by #111.

spatial_ref_sys is owned by the PostGIS extension (supabase_admin on Supabase), so postgres can neither enable RLS on it nor revoke the API-role grants. The supported fix is relocating the whole extension to the extensions schema, which the advisor hard-excludes and PostgREST does not expose.

PostGIS ≥ 2.3 refuses ALTER EXTENSION … SET SCHEMA, so the new migration 20260610_1000 applies the recipe published by the PostGIS project (and reproduced in Supabase's postgis docs): flip pg_extension.extrelocatable, SET SCHEMA extensions, then a dummy UPDATE TO "ANY" + UPDATE to rewrite internal schema-qualified references. Three convergent branches:

  • already relocated / not installed → no-op (prod after the Supabase support step; fresh projects using the dashboard default schema)
  • in public + superuser (docker compose, both CI postgis containers) → run the recipe and persist search_path = "$user", public, extensions so unqualified ST_* calls and future geometry DDL keep resolving (Supabase already ships that search_path)
  • in public + not superuser (Supabase today) → warn loudly and no-op so railway-start.sh deploys never break; per Supabase's own docs the prod relocation must be run by their support team (runbook in docs/POSTGIS-SCHEMA-RELOCATION.md)

Audit found a single geometry column (nws_alerts.geometry — OID-based, survives the move untouched) and runtime-only ST_* usage in aeroza/query/alerts.py and aeroza/push/dispatch.py, all covered by the search_path handling. tests/conftest.py now disposes the engine pool after upgrade head so no pooled test connection keeps a pre-relocation search_path.

Test plan

  • Ephemeral postgis/postgis:16-3.5 (linux/amd64, :55432), image-default DB with topology/tiger preinstalled in public: alembic upgrade head relocates; extension in extensions, spatial_ref_sys 8500 rows, geometry typmod + GIST index intact, unqualified ST_AsGeoJSON/ST_Intersects round trip on a fresh connection
  • alembic downgrade -1 returns postgis to public and resets search_path; re-upgrade relocates again
  • Fresh DB from template1 (CI integration-job path): full upgrade head installs then relocates in one run
  • Supabase simulation (non-superuser role, postgis in public): upgrade reaches head with a warning, postgis untouched, no failure
  • pytest -m integration against the relocated DB: 325 passed
  • make check green (ruff, format, 519 unit tests)
  • CI on this PR (exercises both postgis service containers on amd64)
  • Post-merge: Supabase support ticket per docs/POSTGIS-SCHEMA-RELOCATION.md, then re-run the advisor

…ef_sys lint

public.spatial_ref_sys trips the Supabase advisor's rls_disabled_in_public
(0013) but is owned by the PostGIS extension, so postgres can neither enable
RLS on it nor revoke supabase_admin's grants. The supported fix is moving the
whole extension out of public (also clearing extension_in_public, 0014): the
extensions schema is hard-excluded by the linter and not exposed via
PostgREST.

PostGIS >= 2.3 refuses ALTER EXTENSION ... SET SCHEMA, so migration
20260610_1000 applies the PostGIS-project recipe (flip extrelocatable,
SET SCHEMA, dummy UPDATE TO "ANY" + UPDATE to rewrite internal refs) where it
can, in three convergent branches:

- already relocated / not installed: no-op (prod after the Supabase support
  ticket; fresh projects using the dashboard default schema)
- in public + superuser (docker compose, both CI postgis containers): run the
  recipe and persist search_path = "$user", public, extensions so unqualified
  ST_* calls and future geometry DDL keep resolving (Supabase already ships
  that search_path)
- in public + not superuser (Supabase today): warn and no-op so deploys never
  break; the relocation itself must be run by Supabase support per their docs

docs/POSTGIS-SCHEMA-RELOCATION.md carries the audit, the prod runbook, and
validation evidence. conftest now disposes the engine pool after upgrade head
so no pooled test connection keeps a pre-relocation search_path.

Validated against an ephemeral postgis/postgis:16-3.5 container (amd64,
:55432): image-default DB with topology/tiger preinstalled, fresh DB from
template1, downgrade/re-upgrade cycle, non-superuser Supabase simulation,
and the full integration suite (325 passed) against the relocated DB.
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aeroza-web Ready Ready Preview, Comment Jun 10, 2026 4:02pm

Request Review

@erichare erichare merged commit 5f8cf07 into main Jun 10, 2026
9 checks passed
@erichare erichare deleted the fix/relocate-postgis-extensions-schema branch June 10, 2026 16:07
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.

1 participant