refint is a vintage referential-integrity workaround (committed September 10, 1997! that became pointless the day real foreign keys shipped in PostgreSQL 7.3, twenty-three years ago. It is still in the contrib tree because… well, that’s a good question. And on May 14 it shipped a stack buffer overflow plus SQL injection (CVE-2026-6637, CVSS 8.8) that lets any unprivileged database user run arbitrary code as the OS user the database is running under.
So: patch. Apply the May 14 minor releases (18.4, 17.10, 16.14, 15.18, 14.23) and, if you have refint installed in any database, drop it. That’s the immediate problem.
The larger problem, the one worth a Monday (well, Tuesday) morning conversation, is that almost no team I have talked to in the last decade has been able to answer the question “what extensions are installed in your production databases” without going and looking. Not the ones they meant to install. The ones they actually have.
The contrib tree ages in place
Extensions persist across major-version upgrades. pg_upgrade does the right thing and preserves whatever you had loaded; that is the correct default and I am not suggesting it should change. The consequence is that a database installed in 2014 for a project that has long since been re-architected, then continuously upgraded through 2026, can have modules loaded that nobody on the current team has ever heard of. The CVE does not care whether you remember why it is there.
The contrib tree contains modules at every age and every level of active interest. Some are essential infrastructure: pg_stat_statements, pgcrypto, btree_gin. Some are useful tools you reach for occasionally: pg_buffercache, pageinspect, pg_visibility. Some are pre-foreign-key, pre-window-function curiosities that exist because no one wanted to be the person who deleted someone else’s code: refint, tsm_system_rows, intagg, the seg type, xml2. The PostgreSQL project is conservative about removing things, which is generally correct. It also means the tree is partly a museum.
This generalizes past contrib. Third-party extensions install the same way and persist the same way. A pg_repack from a version ago that nobody is running anymore; a plr somebody loaded for a one-off statistical job in 2019; a forgotten pgaudit configuration that is no longer being shipped to a log aggregator. Same pattern, larger blast radius.
What to do
Run an audit. For each database in each cluster:
1 SELECT e.extname, e.extversion, n.nspname AS schema
2 FROM pg_extension e
3 JOIN pg_namespace n ON n.oid = e.extnamespace
4 ORDER BY e.extname;
Then compare against what’s actually allowed:
1 SELECT name, default_version, installed_version, comment
2 FROM pg_available_extensions
3 WHERE installed_version IS NOT NULL;
And check what’s pre-loaded into every backend:
1 SHOW shared_preload_libraries;
Anything you can’t explain, find an owner for or remove. DROP EXTENSION refint CASCADE if you have it. The CASCADE will tell you what depends on it, which is also useful information.
If you operate at any scale, this needs to be a recurring audit, not a one-time exercise. The natural cadence is “every time we cut a new image” or “every quarterly review,” depending on how your environment is shaped. The mechanism does not matter much; the fact that somebody is doing it does.
For the longer term, somebody on the team should own the list of extensions allowed in production. Treat a new extension request the way you would treat any other production dependency: who maintains it, what is its security history, what happens when it breaks, what is the upgrade story across PostgreSQL major versions. None of this is exotic. It is supply-chain hygiene applied to a part of the stack that most teams have never thought of as a supply chain, which is exactly why the museum keeps growing.
The May 14 CVE will get patched, the news cycle will move on, and most of the lingering risk in your contrib tree will stay exactly where it was. That is the part worth fixing.