Skip to content

Commit cfb3bae

Browse files
NikolaySNik Samokhvalovclaude
authored
fix: revoke execute on pgque.uninstall() from PUBLIC (#60)
pgque.uninstall() was revoked from pgque_admin, but PUBLIC execute was left at the PostgreSQL default for SECURITY DEFINER functions. Any role that could connect to the database could call pgque.uninstall() and drop the entire pgque schema. The README and docs/reference.md both claimed the function was "superuser only"; reality was that every login role had it. Add `revoke execute on function pgque.uninstall() from public;` in sql/pgque-additions/roles.sql alongside the existing pgque_admin revoke. Rebuild sql/pgque.sql. Extend tests/test_pgque_roles.sql to assert that pgque_admin, pgque_writer, and pgque_reader all lack execute on uninstall() — catches any future regression. Found during the functional review of PR #59 (docs restructure). The docs PR tightened the wording to match pre-fix reality and pointed at this PR as the follow-up hardening; once this merges, the docs claim becomes true by default. Co-authored-by: Nik Samokhvalov <nik@niks-mbp.lan> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 3b75f58 commit cfb3bae

3 files changed

Lines changed: 18 additions & 2 deletions

File tree

sql/pgque-additions/roles.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,8 @@ grant all on all tables in schema pgque to pgque_admin;
7777
grant all on all sequences in schema pgque to pgque_admin;
7878
grant execute on all functions in schema pgque to pgque_admin;
7979

80-
-- uninstall() drops the entire schema — only superuser / schema owner should run it
80+
-- uninstall() drops the entire schema — only superuser / schema owner should run it.
81+
-- SECURITY DEFINER functions default to PUBLIC execute; revoke both PUBLIC and
82+
-- pgque_admin so the function really is superuser-only.
83+
revoke execute on function pgque.uninstall() from public;
8184
revoke execute on function pgque.uninstall() from pgque_admin;

sql/pgque.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4239,7 +4239,10 @@ grant all on all tables in schema pgque to pgque_admin;
42394239
grant all on all sequences in schema pgque to pgque_admin;
42404240
grant execute on all functions in schema pgque to pgque_admin;
42414241

4242-
-- uninstall() drops the entire schema — only superuser / schema owner should run it
4242+
-- uninstall() drops the entire schema — only superuser / schema owner should run it.
4243+
-- SECURITY DEFINER functions default to PUBLIC execute; revoke both PUBLIC and
4244+
-- pgque_admin so the function really is superuser-only.
4245+
revoke execute on function pgque.uninstall() from public;
42434246
revoke execute on function pgque.uninstall() from pgque_admin;
42444247

42454248
-- pgque-additions/dlq.sql

tests/test_pgque_roles.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,15 @@ begin
4141
assert has_function_privilege('pgque_writer', 'pgque.nack(bigint, pgque.message, interval, text)', 'EXECUTE'),
4242
'pgque_writer should have execute on nack(bigint, pgque.message, interval, text)';
4343

44+
-- uninstall() must be superuser-only: execute is revoked from both
45+
-- pgque_admin and PUBLIC. Any non-superuser role (including pgque_admin,
46+
-- pgque_writer, pgque_reader) should NOT be able to execute it.
47+
assert not has_function_privilege('pgque_admin', 'pgque.uninstall()', 'EXECUTE'),
48+
'pgque_admin should NOT have execute on uninstall() (revoked in roles.sql)';
49+
assert not has_function_privilege('pgque_writer', 'pgque.uninstall()', 'EXECUTE'),
50+
'pgque_writer should NOT have execute on uninstall() (inherits PUBLIC revoke)';
51+
assert not has_function_privilege('pgque_reader', 'pgque.uninstall()', 'EXECUTE'),
52+
'pgque_reader should NOT have execute on uninstall() (inherits PUBLIC revoke)';
53+
4454
raise notice 'PASS: pgque_roles';
4555
end $$;

0 commit comments

Comments
 (0)