diff --git a/supabase/drift-manifest.json b/supabase/drift-manifest.json index acf341110..b28911347 100644 --- a/supabase/drift-manifest.json +++ b/supabase/drift-manifest.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-07-13T08:06:40.593Z", + "generated_at": "2026-07-13T08:13:14.832Z", "generator": "scripts/generate-drift-manifest.ts", "postgres_image": "supabase/postgres:17.6.1.127", "schema_sha256": "0a30a1ea8c8dbb3fd520115d28a17d99c94d9bc5164efe6c287c3b48969be81b", diff --git a/supabase/migrations/20260708150150_harden_retrieval_public_execute.sql b/supabase/migrations/20260708150150_harden_retrieval_public_execute.sql new file mode 100644 index 000000000..1f9c9bd92 --- /dev/null +++ b/supabase/migrations/20260708150150_harden_retrieval_public_execute.sql @@ -0,0 +1,15 @@ +-- Codify the migration version already recorded on production. These two +-- retrieval helpers are server-only, so their EXECUTE posture matches the +-- schema-wide service-role-only policy in supabase/schema.sql. + +set search_path = public, extensions, pg_temp; + +revoke execute on function public.retrieval_owner_matches(uuid, uuid) + from public, anon, authenticated; +grant execute on function public.retrieval_owner_matches(uuid, uuid) + to service_role; + +revoke execute on function public.search_document_chunks(uuid, text, integer, uuid) + from public, anon, authenticated; +grant execute on function public.search_document_chunks(uuid, text, integer, uuid) + to service_role; diff --git a/supabase/migrations/20260709062443_reconcile_ingestion_rpc_privileges_production.sql b/supabase/migrations/20260709062443_reconcile_ingestion_rpc_privileges_production.sql new file mode 100644 index 000000000..3d81db40d --- /dev/null +++ b/supabase/migrations/20260709062443_reconcile_ingestion_rpc_privileges_production.sql @@ -0,0 +1,14 @@ +-- Codify the migration version already recorded on production. The functions +-- are worker-only and must not retain PostgreSQL's default PUBLIC EXECUTE. + +set search_path = public, pg_catalog, pg_temp; + +revoke execute on function public.complete_ingestion_job(uuid, uuid, uuid, text, text) + from public, anon, authenticated; +grant execute on function public.complete_ingestion_job(uuid, uuid, uuid, text, text) + to service_role; + +revoke execute on function public.fail_or_retry_ingestion_job(uuid, uuid, uuid, boolean, text, text, text, timestamp with time zone, text) + from public, anon, authenticated; +grant execute on function public.fail_or_retry_ingestion_job(uuid, uuid, uuid, boolean, text, text, text, timestamp with time zone, text) + to service_role; diff --git a/supabase/migrations/20260709150000_reconcile_ingestion_rpc_privileges.sql b/supabase/migrations/20260709150000_reconcile_ingestion_rpc_privileges.sql index 832ea3fc2..2d215ee41 100644 --- a/supabase/migrations/20260709150000_reconcile_ingestion_rpc_privileges.sql +++ b/supabase/migrations/20260709150000_reconcile_ingestion_rpc_privileges.sql @@ -1,12 +1,8 @@ --- Reconcile ingestion RPC permissions (revoke execute from public) --- Recent migration 20260708130000 dropped and recreated complete_ingestion_job and fail_or_retry_ingestion_job. --- Since migrations run as supabase_admin on Supabase, the default privileges for role postgres --- do not apply. We must explicitly revoke PUBLIC execution on these new signatures. +-- NEUTRALIZED 2026-07-13 -- DO NOT DELETE THIS FILE. +-- +-- Production recorded this ACL reconciliation under its generated migration +-- version, 20260709062443. That exact migration now lives in the repository. +-- Keep this later duplicate stem as a no-op so branches that previously saw it +-- retain monotonic history without applying the same grants twice. -set search_path = public, pg_catalog, pg_temp; - -revoke execute on function public.complete_ingestion_job(uuid, uuid, uuid, text, text) from public, anon, authenticated; -grant execute on function public.complete_ingestion_job(uuid, uuid, uuid, text, text) to service_role; - -revoke execute on function public.fail_or_retry_ingestion_job(uuid, uuid, uuid, boolean, text, text, text, timestamp with time zone, text) from public, anon, authenticated; -grant execute on function public.fail_or_retry_ingestion_job(uuid, uuid, uuid, boolean, text, text, text, timestamp with time zone, text) to service_role; +select 1 where false; diff --git a/tests/supabase-schema.test.ts b/tests/supabase-schema.test.ts index 478018379..d22e952a0 100644 --- a/tests/supabase-schema.test.ts +++ b/tests/supabase-schema.test.ts @@ -54,6 +54,18 @@ const ingestionJobsOneOpenMigration = readFileSync( new URL("../supabase/migrations/20260708170000_ingestion_jobs_one_open_per_document.sql", import.meta.url), "utf8", ).replace(/\s+/g, " "); +const retrievalPublicExecuteMigration = readFileSync( + new URL("../supabase/migrations/20260708150150_harden_retrieval_public_execute.sql", import.meta.url), + "utf8", +).replace(/\s+/g, " "); +const ingestionRpcPrivilegesMigration = readFileSync( + new URL("../supabase/migrations/20260709062443_reconcile_ingestion_rpc_privileges_production.sql", import.meta.url), + "utf8", +).replace(/\s+/g, " "); +const ingestionRpcPrivilegesDuplicateMigration = readFileSync( + new URL("../supabase/migrations/20260709150000_reconcile_ingestion_rpc_privileges.sql", import.meta.url), + "utf8", +).replace(/\s+/g, " "); const atomicReindexMigration = readFileSync( new URL("../supabase/migrations/20260628000000_atomic_reindex_generation_commit.sql", import.meta.url), "utf8", @@ -954,4 +966,21 @@ describe("Supabase Preview replay guards", () => { expect(ingestionJobsOneOpenMigration).toContain("ingestion_jobs_one_open_per_document_uidx"); expect(ingestionJobsOneOpenMigration).not.toContain("concurrently"); }); + + it("codifies production ACL migration versions and neutralizes the later duplicate", () => { + expect(retrievalPublicExecuteMigration).toContain( + "revoke execute on function public.retrieval_owner_matches(uuid, uuid)", + ); + expect(retrievalPublicExecuteMigration).toContain( + "revoke execute on function public.search_document_chunks(uuid, text, integer, uuid)", + ); + expect(ingestionRpcPrivilegesMigration).toContain( + "revoke execute on function public.complete_ingestion_job(uuid, uuid, uuid, text, text)", + ); + expect(ingestionRpcPrivilegesMigration).toContain( + "revoke execute on function public.fail_or_retry_ingestion_job(uuid, uuid, uuid, boolean, text, text, text, timestamp with time zone, text)", + ); + expect(ingestionRpcPrivilegesDuplicateMigration).toContain("NEUTRALIZED 2026-07-13"); + expect(ingestionRpcPrivilegesDuplicateMigration).toContain("select 1 where false;"); + }); });