Pin `search_path` on operator-issued connections
Committed by armru
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 message carries the incident narrative: shadow operators planted in public plus an ALTER DATABASE or ALTER ROLE search_path change, resolving ahead of pg_catalog during superuser probes and ending in in-pod RCE via COPY FROM PROGRAM. Linking the class to CVE-2018-1058 gives readers prior art, and the remediation reads as rationale rather than changelog: pin the path as a pgx RuntimeParam so it ships in the StartupMessage and outranks tenant defaults. Closing bypasses matters too - routing the three raw sql.Open callsites through the pool, bracketing user scripts, and pinning the SECURITY DEFINER function for the pgbouncer path. Only the cherry-pick provenance and repeated sign-offs clutter it.