fix(pg-delta): drop PG16 implicit createrole ADMIN memberships from extraction - #461
fix(pg-delta): drop PG16 implicit createrole ADMIN memberships from extraction#461avallete wants to merge 4 commits into
Conversation
…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 detectedLatest commit: 06c3e3d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
commit: |
There was a problem hiding this comment.
💡 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".
There was a problem hiding this comment.
💡 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".
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.
There was a problem hiding this comment.
💡 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".
| fact.id.member === cap.role && | ||
| fact.payload["admin"] === true | ||
| ) { | ||
| roots.set(encodeId(fact.id), CAPABILITY_CREATEROLE_SELF_ADMIN); |
There was a problem hiding this comment.
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 👍 / 👎.
| fact.id.member === cap.role && | ||
| fact.payload["admin"] === true | ||
| ) { | ||
| roots.set(encodeId(fact.id), CAPABILITY_CREATEROLE_SELF_ADMIN); |
There was a problem hiding this comment.
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 👍 / 👎.
| createRole?: boolean; | ||
| /** Server major (e.g. 16). Omitted on legacy artifacts / hand-built fixtures. */ | ||
| pgMajor?: number; |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
On PG16+ a CREATEROLE non-superuser (Supabase
postgres) that runsCREATE ROLE xautomatically receivesGRANT x TO postgres WITH ADMIN OPTIONwhose 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:That failed the entire
useInitMigrationTaskbaseline (Sentry SUPABASE-WORKERS-6RX). Shadow load already strips these rows viabootstrapMembershipStrip; this PR applies the same contract to live extract.MEMBERSHIPS_SQLnow drops membership rows where grantor is oid 10, member iscurrent_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).
open-for-contributionlabel (or I'm a Supabase maintainer).Test plan
role-created-by-createrole-user(forward) failed with the exact ADMIN OPTION message / SQLSTATE 0LP01bun test src/(1394 pass)minVersion: 16)bun run format-and-lintandbun run check-typesChecklist
bunx changeset)bun run format-and-lintandbun run check-typespass