Skip to content

fix(pg-delta): drop PG16 implicit createrole ADMIN memberships from extraction - #461

Open
avallete wants to merge 4 commits into
mainfrom
fix/pg-delta-createrole-implicit-admin
Open

fix(pg-delta): drop PG16 implicit createrole ADMIN memberships from extraction#461
avallete wants to merge 4 commits into
mainfrom
fix/pg-delta-createrole-implicit-admin

Conversation

@avallete

@avallete avallete commented Sep 7, 2026

Copy link
Copy Markdown
Member

Summary

On PG16+ a CREATEROLE non-superuser (Supabase postgres) that runs CREATE ROLE x automatically receives GRANT x TO postgres WITH ADMIN OPTION whose grantor is the bootstrap superuser (oid 10). Live extraction was grantor-blind, so a baseline from that project planned the GRANT against an empty branch and Postgres rejected it:

ADMIN option cannot be granted back to your own grantor

That failed the entire useInitMigrationTask baseline (Sentry SUPABASE-WORKERS-6RX). Shadow load already strips these rows via bootstrapMembershipStrip; this PR applies the same contract to live extract.

MEMBERSHIPS_SQL now drops membership rows where grantor is oid 10, member is current_user, and that user is CREATEROLE and not a superuser. Explicit memberships granted by the applier (grantor = applier) are still extracted and planned.

Linked issue

No GitHub issue. Linear / incident: CLI-2297, BRA-278, CLI-2296 (batched apply makes the failure cheaper but does not remove it).

  • The linked issue is open and carries the open-for-contribution label (or I'm a Supabase maintainer).

Test plan

  • RED: integration test and corpus role-created-by-createrole-user (forward) failed with the exact ADMIN OPTION message / SQLSTATE 0LP01
  • GREEN: same tests pass after the extract filter
  • bun test src/ (1394 pass)
  • PG16 corpus 668/668
  • PG15 corpus 668/668 (new scenario skipped via minVersion: 16)
  • bun run format-and-lint and bun run check-types

Checklist

  • Tests added or updated for the change
  • Changeset added if this is a user-facing fix/feature (bunx changeset)
  • bun run format-and-lint and bun run check-types pass

…ership

A CREATEROLE applier that CREATE ROLEs on PG16+ records GRANT … TO <self>
WITH ADMIN OPTION from the bootstrap superuser. Live extract kept that row,
so apply to an empty branch failed with SQLSTATE 0LP01
("ADMIN option cannot be granted back to your own grantor").
…tion

Skip pg_auth_members rows granted by the bootstrap superuser (oid 10) to
the current CREATEROLE non-superuser so live extract matches the
shadow-load strip and CREATE ROLE is not followed by a failing self-GRANT.
@changeset-bot

changeset-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 06c3e3d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@supabase/pg-delta Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 7, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/supabase/pg-toolbelt/@supabase/pg-delta@461
npm i https://pkg.pr.new/supabase/pg-toolbelt/@supabase/pg-topo@461

commit: 06c3e3d

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38f6d16273

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/pg-delta/src/extract/roles.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2acf1456df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/pg-delta/src/extract/roles.ts Outdated
@avallete
avallete requested a review from jgoux September 7, 2026 16:48
Live extract stays catalog-true. A PG16+ CREATEROLE applier cannot GRANT a
role to itself WITH ADMIN OPTION (0LP01); capability projection drops those
facts so CREATE ROLE is not followed by a failing self-GRANT.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06c3e3d395

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +93 to +96
fact.id.member === cap.role &&
fact.payload["admin"] === true
) {
roots.set(encodeId(fact.id), CAPABILITY_CREATEROLE_SELF_ADMIN);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate the runtime applier before using stamped capability

When a --restrict-to-applier plan is generated as role A but later passed to the standalone pgdelta apply command using role B, this projection still suppresses A's ADMIN membership from the artifact while PostgreSQL's CREATE ROLE implicitly grants the new role to B. The pre-apply fingerprint gate also reconstructs with thePlan.capability and never compares it with the target's current_user, so it passes and apply reports success even though the resulting membership state differs from the desired state. Validate the applying role/server capability against the stamped probe before executing such a plan (or re-probe and refuse a mismatch).

AGENTS.md reference: AGENTS.md:L88-L90

Useful? React with 👍 / 👎.

Comment on lines +93 to +96
fact.id.member === cap.role &&
fact.payload["admin"] === true
) {
roots.set(encodeId(fact.id), CAPABILITY_CREATEROLE_SELF_ADMIN);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve ADMIN-option downgrade semantics

When the source contains a self-membership with admin: true and the desired state keeps the same membership with admin: false, this predicate removes only the source fact. Planning therefore sees an addition and emits plain GRANT role TO applier instead of the existing membership rule's REVOKE ADMIN OPTION FOR ...; a plain grant does not clear the ADMIN option, so apply cannot reach the retained non-admin desired state. Fresh evidence versus the prior comments is the new admin-only predicate: it makes the two payload versions of the same stable identity project asymmetrically.

AGENTS.md reference: AGENTS.md:L88-L90

Useful? React with 👍 / 👎.

Comment on lines +32 to +34
createRole?: boolean;
/** Server major (e.g. 16). Omitted on legacy artifacts / hand-built fixtures. */
pgMajor?: number;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bind the new capability fields into the plan digest

These fields control whether membership facts are removed during the apply fingerprint gate, but computePlanId does not include Plan.capability in its hashed payload and parsePlan does not validate this object's shape. Consequently, changing a serialized artifact from createRole: false/PG15 to createRole: true/PG16 still passes both parsePlan and assertPlanId while changing which target drift is suppressed; an unexpected self-ADMIN membership can therefore be hidden without invalidating the artifact's approval digest. Include the projection-affecting capability in the plan ID and validate it when parsing.

Useful? React with 👍 / 👎.

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.

2 participants