Skip to content

fix(0002): constrain pg_depend classid/refclassid to avoid cross-catalog oid collisions - #177

Open
karpovantonme wants to merge 1 commit into
supabase:mainfrom
karpovantonme:fix/0002-refclassid-cross-catalog-oid-collision
Open

fix(0002): constrain pg_depend classid/refclassid to avoid cross-catalog oid collisions#177
karpovantonme wants to merge 1 commit into
supabase:mainfrom
karpovantonme:fix/0002-refclassid-cross-catalog-oid-collision

Conversation

@karpovantonme

Copy link
Copy Markdown

Fixes #171.

Problem

0002_auth_users_exposed joins pg_depend to auth.users on refobjid alone:

join pg_catalog.pg_depend d
    on d.refobjid = auth_users_pg_class.oid

pg_depend.objid / refobjid are only unique within a (classid, refclassid) pair. Without those predicates, a dependency row that points at an object in a different catalog whose oid happens to equal auth.users' pg_class oid is matched too.

In the reported case a public view called a SECURITY DEFINER helper function whose pg_proc oid equalled auth.users' pg_class oid (16499). The view selects only from public tables and has no relation dependency on auth.users, yet it was reported as CRITICAL auth_users_exposed and triggered an "action required: security vulnerabilities" email.

Fix

Constrain both sides of the join:

join pg_catalog.pg_depend d
    on d.refobjid = auth_users_pg_class.oid
    and d.refclassid = 'pg_catalog.pg_class'::regclass
    and d.classid = 'pg_catalog.pg_rewrite'::regclass

This matches the filters the other lints already carry after #166 ("objid is only unique within a classid"), which added classid predicates to 0001, 0004-0011, 0016 and 0017 but did not touch this join in 0002.

Also drops the redundant join pg_catalog.pg_class pg_class_auth_users on d.refobjid = pg_class_auth_users.oid. Once refclassid is constrained it always resolves to the same row as auth_users_pg_class, which is already restricted to auth.users, so relrowsecurity is read from that alias instead.

No change to the lint's true positives: a view or materialized view whose rewrite really depends on auth.users still carries classid = pg_rewrite and refclassid = pg_class.

Test

An oid collision cannot be produced by waiting for one, so the regression case forces it inside the test transaction: a function is created, its pg_proc oid is moved onto auth.users' pg_class oid, and a view that calls the function (and never references auth.users) is created. The lint must return 0 rows. On main that view is reported; with this change it is not. Everything runs inside the existing begin / rollback.

splinter.sql regenerated via bin/compile.py.

Verification

  • docker compose -f dockerfiles/docker-compose.yml run --rm test on supabase/postgres:15.1.1.13 — all 28 tests pass.
  • pre-commit run --all-files — all hooks pass.

…log oid collisions

`0002_auth_users_exposed` joined pg_depend to auth.users on refobjid alone:

    join pg_catalog.pg_depend d
        on d.refobjid = auth_users_pg_class.oid

pg_depend.objid/refobjid are only unique within a (classid, refclassid), so a
dependency row that points at an object in a different catalog whose oid happens
to equal auth.users' pg_class oid matches as well. In the reported case a view
called a SECURITY DEFINER function whose pg_proc oid equalled auth.users'
pg_class oid (16499); the view selects only from public tables, yet it was
reported as CRITICAL auth_users_exposed and triggered a security advisory email.

Add `d.refclassid = 'pg_catalog.pg_class'::regclass` and
`d.classid = 'pg_catalog.pg_rewrite'::regclass`, the same filters the other
lints already carry after supabase#166.

Also drop the redundant `pg_class pg_class_auth_users` join. Once refclassid is
constrained it always resolves to the same row as `auth_users_pg_class`, which
is already restricted to auth.users.

The regression test forces the collision by moving a function's pg_proc oid onto
auth.users' pg_class oid inside the test transaction, then asserts the view that
calls it is not reported.

splinter.sql regenerated via bin/compile.py.

Closes supabase#171
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.

0002_auth_users_exposed false positive: pg_depend join lacks a refclassid filter (cross-catalog OID collision)

1 participant