Pin `search_path` on operator-issued connections

fix(security): pin `search_path` on operator-issued connections (#10774)
Pin `search_path` on operator-issued connections02b5c6-fix-pin-search-path-on-operator-issued-connections-cve-2026-55769fixpin search path operator issued connections

Committed by armru

commit message
fix(security): pin `search_path` on operator-issued connections (#10774)

A database owner could plant overloaded built-in operators (`=`, `>`,
...) in the `public` schema and `ALTER DATABASE` / `ALTER ROLE` the
`search_path` so that operator introspection probes, running as the
cluster superuser, resolved those overloads before `pg_catalog`,
yielding superuser access and in-pod RCE via `COPY ... FROM PROGRAM`
(`CWE-426`, same class as `CVE-2018-1058`).
 
Pin `search_path = pg_catalog, public, pg_temp` as a `pgx`
`RuntimeParam` on every pooled operator connection so it ships in the
`StartupMessage` and takes precedence over tenant-controlled
database/role defaults: `pg_catalog` first defeats any planted shadow,
`public` stays writable for `CREATE EXTENSION`, and `pg_temp` is
searched last. The `pgbouncer` profile opts out, as its admin console
rejects unknown startup parameters.
 
Close the remaining bypasses and user-facing paths:
 
- Route the three direct `sql.Open("pgx", ...)` callsites
(`WaitForPrimaryAvailable`, the two WAL-archive helpers) through
`pool.NewDBConnection` so they inherit the pin.
- Bracket user-authored scripts (`postInit*`, logical-import
`PostImportApplicationSQL`) and `CREATE EXTENSION` with a dedicated
connection that `SET`s `"$user", public` and `RESET`s back to the pinned
default,
  so they keep standard resolution without leaking it to the pool.
- Pin `search_path = pg_catalog, pg_temp` on the `public.user_search`
`SECURITY DEFINER` function (reached by `pgbouncer` via `libpq`, outside
the pool) and require `proconfig IS NOT NULL` so existing clusters are
upgraded.
- Replace the monitoring transaction's `set_config` workaround with an
explicit `SET LOCAL search_path = pg_catalog, public, pg_temp`.
 
Add an e2e regression test that plants a shadow `=` operator and flips
the database `search_path`, plus unit tests.
 
Updates the documentation accordingly.
 
Signed-off-by: Armando Ruocco <[email protected]>
Signed-off-by: Gabriele Bartolini <[email protected]>
Signed-off-by: Marco Nenciarini <[email protected]>
Co-authored-by: Mehmet Ince <[email protected]>
Co-authored-by: Gabriele Bartolini <[email protected]>
Co-authored-by: Marco Nenciarini <[email protected]>
(cherry picked from commit db38f4d80315c8f1b21bf511ef0f28871820c14d)

Editor assessment

This commit patches CVE-2026-55769. The subject is precise, and the body earns the top grade by explaining the attack first: a database owner plants overloaded built-in operators that resolve ahead of pg_catalog during superuser introspection, yielding RCE through COPY FROM PROGRAM. It justifies the chosen ordering of `pg_catalog, public, pg_temp`, notes the pgbouncer opt-out because that pooler rejects unknown startup parameters, and enumerates the remaining bypasses it closes, including the SECURITY DEFINER function reached outside the pool. Regression tests and documentation updates are called out. The only rough edges are the cherry-pick provenance line and the repeated signed-off trailers at the end.

Browse categories

More fix examples · Back to search