Summary
Round-2 raw SQL security testing found that PgQue's role model is porous because most functions keep PostgreSQL's default PUBLIC EXECUTE privilege.
Although docs describe pgque_reader, pgque_writer, and pgque_admin as distinct access levels, a read-only / ungranted role can execute writer/admin-ish SECURITY DEFINER functions such as create_queue, drop_queue, and send.
Impact
A role intended to be read-only can mutate PgQue state:
- create queues
- drop queues
- send events
- possibly manage consumers / config / lifecycle functions depending on schema usage and function grants
This weakens the intended pgque_reader / pgque_writer / pgque_admin separation.
Repro
\i sql/pgque.sql
create role r_reader login;
grant pgque_reader to r_reader;
set role r_reader;
select
has_schema_privilege(current_user, 'pgque', 'USAGE') as schema_usage,
has_function_privilege(current_user, 'pgque.create_queue(text)', 'EXECUTE') as can_create,
has_function_privilege(current_user, 'pgque.drop_queue(text)', 'EXECUTE') as can_drop,
has_function_privilege(current_user, 'pgque.send(text,text,text)', 'EXECUTE') as can_send;
select pgque.create_queue('r_reader_created');
reset role;
Observed:
schema_usage | can_create | can_drop | can_send
-------------+------------+----------+---------
t | t | t | t
create_queue
------------
1
Also verified with an ungranted PUBLIC role in a local probe:
PASS: ungranted PUBLIC role can create queue
PASS: ungranted PUBLIC role can drop queue it did not own
Expected
pgque_reader should not be able to execute writer/admin mutating APIs.
- Ungranted PUBLIC roles should not be able to create/drop queues or mutate PgQue state.
Suggested fix
Use deny-by-default function privileges:
revoke execute on all functions in schema pgque from public;
Then explicitly grant the intended functions to each role:
- reader: introspection/read-only functions only
- writer: send/receive/ack/nack/subscribe APIs and necessary PgQ primitives
- admin: queue lifecycle/config/maintenance functions
- uninstall: keep superuser/schema-owner only
Need to account for functions created after blanket revokes too, especially colocated API/DLQ functions.
Tests to add
Add a role matrix test using actual SET ROLE, not only has_function_privilege:
- reader cannot execute:
create_queue, drop_queue, send, receive, ack, nack, dlq_replay, set_queue_config
- writer can execute intended producer/consumer APIs but cannot purge/drop/admin config if policy says so
- admin can execute admin functions
- PUBLIC cannot mutate PgQue
Evidence
Tested on main at 9b3f89f.
Existing tests/run_all.sql passed before this probe.
Summary
Round-2 raw SQL security testing found that PgQue's role model is porous because most functions keep PostgreSQL's default
PUBLIC EXECUTEprivilege.Although docs describe
pgque_reader,pgque_writer, andpgque_adminas distinct access levels, a read-only / ungranted role can execute writer/admin-ishSECURITY DEFINERfunctions such ascreate_queue,drop_queue, andsend.Impact
A role intended to be read-only can mutate PgQue state:
This weakens the intended
pgque_reader/pgque_writer/pgque_adminseparation.Repro
Observed:
Also verified with an ungranted PUBLIC role in a local probe:
Expected
pgque_readershould not be able to execute writer/admin mutating APIs.Suggested fix
Use deny-by-default function privileges:
Then explicitly grant the intended functions to each role:
Need to account for functions created after blanket revokes too, especially colocated API/DLQ functions.
Tests to add
Add a role matrix test using actual
SET ROLE, not onlyhas_function_privilege:create_queue,drop_queue,send,receive,ack,nack,dlq_replay,set_queue_configEvidence
Tested on
mainat9b3f89f.Existing
tests/run_all.sqlpassed before this probe.