Skip to content

Limit ext version#187

Open
samrose wants to merge 4 commits intomasterfrom
limit-ext-version
Open

Limit ext version#187
samrose wants to merge 4 commits intomasterfrom
limit-ext-version

Conversation

@samrose
Copy link
Copy Markdown

@samrose samrose commented Apr 14, 2026

Summary

  • Add restrict_version_specification() helper to supautils ProcessUtility hook that blocks non-superusers from using VERSION in CREATE EXTENSION or TO in ALTER EXTENSION UPDATE
  • Only the configured supautils.superuser role and actual PostgreSQL superusers are exempt
  • Update existing tests that used versioned CREATE EXTENSION as non-superusers

Motivation

Supabase ships multiversion extensions with multiple versions installed simultaneously. Without this restriction, any user who can create extensions can pin to an older, non-default version via CREATE EXTENSION foo VERSION '1.2.3' or ALTER EXTENSION foo UPDATE TO '1.2.3'. This change ensures only platform operators can specify versions, while regular users get the current default.

Changes

  • src/supautils.c: new restrict_version_specification() function called in both T_CreateExtensionStmt and T_AlterExtensionStmt cases, before any privilege escalation or custom scripts
  • test/sql/restrict_version.sql + test/expected/restrict_version.out: new test covering blocked VERSION/TO clauses for non-superusers, allowed bare CREATE/ALTER EXTENSION, and supautils.superuser bypass
    path
  • test/init.conf.in: add supautils.superuser GUC for tests
  • test/init.sql: add superuser_for_test role (nosuperuser) to exercise the name comparison bypass
  • test/sql/extension_custom_scripts.sql + .out: switch to superuser for versioned CREATE EXTENSION that would now be blocked
  • test/expected/privileged_role.out.in: update expected error from 22023 to 42501 since the version restriction fires before PostgreSQL's own version validation

Test plan

  • xpg -v 17 test passes all tests
  • xpg -v 16 test passes all tests
  • xpg -v 15 test passes all tests
  • xpg -v 14 test passes all tests
  • xpg -v 13 test passes all tests

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 15, 2026

Coverage Report for CI Build 24445324940

Coverage increased (+0.2%) to 88.942%

Details

  • Coverage increased (+0.2%) from the base build.
  • Patch coverage: 2 uncovered changes across 1 file (12 of 14 lines covered, 85.71%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
src/supautils.c 14 12 85.71%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1257
Covered Lines: 1118
Line Coverage: 88.94%
Coverage Strength: 33.54 hits per line

💛 - Coveralls

@samrose samrose marked this pull request as ready for review April 20, 2026 14:54
@steve-chavez
Copy link
Copy Markdown
Member

steve-chavez commented Apr 20, 2026

@samrose Whatever feature we add to supautils it must be configurable. Also I wonder how future-proof is this change. Assume we want to provide a pg_graphql 4.5 as the minimally required version (because of security issues) but then we release 5.0 with new features that users want and we don't want to force them to upgrade right away because it's breaking.

So the most reasonable thing to me is to provide a range of versions allowed by the admin.

For this we can take advantage of the lexicographic order for text in postgres:

postgres=# select '3.3' < '4';
 ?column? 
----------
 t
(1 row)

postgres=# select '4.3'::text < '4.4'::text;
 ?column? 
----------
 t
(1 row)

postgres=# select '4.3'::text < '4.3.1'::text;
 ?column? 
----------
 t
(1 row)

Given that I suggest the config be something like:

supautils.limited_version_extensions = '{"plrust": ["3.3", "4.5"], "any_extension_name": ["1.0", "2.0"]}'

So extensions are keys and the range is defined by an array of dimension 2. Our constrained-extensions has a similar json config.

@steve-chavez
Copy link
Copy Markdown
Member

steve-chavez commented Apr 20, 2026

@samrose There's also another simplification with a caveat. We can reuse the numrange type to simplify the configuration like:

supautils.limited_version_extensions = '{"plrust": "(1.1,2)", "another": "(4.3,)"}'

What is great is that with this you can leave a minimum version without specifying a maximum, remember that:

select 4.2 <@ '[1.1,)'::numrange;
 ?column? 
----------
 t
(1 row)

And with the array JSON config we cannot do that unless we also parse arrays of length 1.

The caveat is that this doesn't work for PATCH versions, so you could not specify a minimum of 4.6.4 (invalid numeric). Not sure how we're vendoring versions, if we only pick the latest PATCH for a minor (sane default) then it should work fine.

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.

3 participants