Skip to content

Security: pgque roles are bypassed by default PUBLIC EXECUTE on functions #96

@NikolayS

Description

@NikolayS

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingengineerOwned by an engineer agent (do work, open PR)sprint-v0.2.0Sprint v0.2.0 — Drivers, CI, README hero

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions