Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ else
endif

EXTENSION = pg_net
EXTVERSION = 0.20.3
EXTVERSION = 0.20.4

DATA = $(wildcard sql/*--*.sql)

Expand Down
1 change: 1 addition & 0 deletions sql/pg_net--0.20.3--0.20.4.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
revoke trigger on all tables in schema net from PUBLIC;
4 changes: 4 additions & 0 deletions sql/pg_net.sql
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,7 @@ $$;
grant usage on schema net to PUBLIC;
grant all on all sequences in schema net to PUBLIC;
grant all on all tables in schema net to PUBLIC;

-- we do not want users to be able to create triggers on tables in the net schema to avoid
-- privilege escalation bugs.
revoke trigger on all tables in schema net from PUBLIC;
54 changes: 54 additions & 0 deletions test/test_privileges.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,57 @@ def test_net_on_new_role(sess):
set local role postgres;
drop role another;
"""))


def test_public_roles_cannot_create_triggers_on_internal_tables(sess):
"""Internal tables must not allow trigger-based privilege escalation."""

(can_select, can_trigger) = sess.execute(text("""
select
has_table_privilege('pre_existing', 'net._http_response', 'select'),
has_table_privilege('pre_existing', 'net._http_response', 'trigger');
""")).fetchone()

assert can_select is True
assert can_trigger is False

sess.execute(text("""
create table public.t1 (id text);

create or replace function public.my_trigger_fn()
returns trigger
language plpgsql
as $$
begin
insert into public.t1 (id) values (current_user);
return new;
end
$$;
"""))
sess.commit()

with pytest.raises(Exception) as execinfo:
sess.execute(text("""
set local role to pre_existing;

create trigger my_trigger
after insert on net.http_request_queue
for each row
execute function public.my_trigger_fn();
"""))

assert "permission denied for table http_request_queue" in str(execinfo.value)

sess.rollback()

with pytest.raises(Exception) as execinfo:
sess.execute(text("""
set local role to pre_existing;

create trigger my_trigger
after insert on net._http_response
for each row
execute function public.my_trigger_fn();
"""))

assert "permission denied for table _http_response" in str(execinfo.value)
Loading