CVE-2026-58225 Overview
CVE-2026-58225 is a SQL injection vulnerability in the Postgrex.Notifications module of the elixir-ecto postgrex PostgreSQL driver [CWE-89]. An attacker who can influence the value passed as a LISTEN channel name can inject content into the reconnect replay query. This breaks the notification connection for every channel sharing it, producing a denial of service. Because quote_channel/1 doubles internal double quotes, arbitrary SQL execution is not possible. The issue affects postgrex from version 0.16.0 before 0.22.3.
Critical Impact
Untrusted input reaching Postgrex.Notifications.listen/3 can silently drop notifications for all tenants sharing the connection, breaking any application logic that depends on PostgreSQL LISTEN/NOTIFY.
Affected Products
- elixir-ecto postgrex0.16.0 through 0.22.2
- Elixir/Erlang applications using Postgrex.Notifications.listen/3 with untrusted channel names
- Downstream libraries embedding postgrex in the affected version range (including ecto_sql deployments)
Discovery Timeline
- 2026-07-10 - CVE-2026-58225 published to NVD
- 2026-07-10 - Last updated in NVD database
Technical Details for CVE-2026-58225
Vulnerability Analysis
Postgrex.Notifications sanitizes channel names using quote_channel/1, which doubles embedded double quotes so the name is safe inside a double-quoted identifier. This protects single-statement LISTEN and UNLISTEN paths. However, on every connect and reconnect, handle_connect/1 replays all registered channels at once. It concatenates their LISTEN statements and wraps them in a dollar-quoted anonymous code block of the form DO $$BEGIN ... END$$.
quote_channel/1 does not escape the $$ dollar-quote delimiter that opens and closes this block. The listen/3 guards only reject null bytes and names longer than 63 bytes, so a channel name containing $$ passes validation unchanged. Once embedded, the $$ sequence prematurely terminates the outer dollar-quoted string, and PostgreSQL parses the remainder as additional top-level statements.
Because handle_connect/1 runs on every reconnect, the malformed replay query is rejected each time. The notification connection never re-establishes its subscriptions, silently dropping notifications for every channel sharing that connection.
Root Cause
The root cause is inconsistent quoting between single-statement paths and the batched replay query. quote_channel/1 handles double-quoted identifier escaping but leaves dollar-quote delimiters untouched, allowing the wrapping DO $$...$$ block to be broken by a channel name containing $$.
Attack Vector
An application is affected when it passes untrusted input, such as a tenant or user identifier, as the channel name argument to Postgrex.Notifications.listen/3. Because double-quote doubling prevents forming a fully valid injected statement, arbitrary SQL execution is not possible. The corrupted query reliably breaks the shared notification connection for all tenants, producing denial of service of the LISTEN/NOTIFY subsystem.
|> Map.keys()
|> Enum.map_join("\n", &~s(LISTEN #{quote_channel(&1)};))
- query = "DO $$BEGIN #{listen_statements} END$$"
+ query = "DO #{dollar_quote("BEGIN #{listen_statements} END")}"
{:query, query, state}
else
Source: GitHub commit 795c6062. The patch replaces the static $$...$$ wrapper with a dollar_quote/1 helper that selects a delimiter tag not present in the payload.
Detection Methods for CVE-2026-58225
Indicators of Compromise
- PostgreSQL server logs showing repeated syntax errors on DO $$BEGIN LISTEN ... statements originating from postgrex connection setup.
- Application logs reporting failed reconnects on Postgrex.Notifications processes accompanied by dropped notification delivery.
- Channel names in application inputs or database records containing $$ sequences.
Detection Strategies
- Audit source code for calls to Postgrex.Notifications.listen/3 where the channel argument derives from user, tenant, or request-scoped input.
- Inspect mix.lock and dependency manifests for postgrex versions between 0.16.0 and 0.22.2 inclusive.
- Enable PostgreSQL log_min_error_statement = error and alert on DO block syntax errors correlated with client reconnects.
Monitoring Recommendations
- Track notification delivery latency and consumer-side gaps to detect silent subscription loss after reconnect events.
- Monitor postgrex client connection lifecycle telemetry for repeated failures on the same notification connection.
- Alert on any channel name value passed to listen/3 that contains $ characters until dependencies are patched.
How to Mitigate CVE-2026-58225
Immediate Actions Required
- Upgrade postgrex to version 0.22.3 or later across all applications and services.
- Inventory every call site of Postgrex.Notifications.listen/3 and confirm whether channel names originate from untrusted input.
- Until patched, reject or sanitize channel names containing $ characters at the application boundary.
Patch Information
The fix is available in postgrex0.22.3, published in commit 795c6062. The patch replaces the hardcoded $$ dollar-quote delimiter in handle_connect/1 with a dollar_quote/1 helper that chooses a delimiter tag guaranteed not to appear in the wrapped payload. See the GitHub Security Advisory GHSA-4mw9-4qgj-m97w and the OSV record EEF-CVE-2026-58225 for full advisory metadata.
Workarounds
- Validate channel names against a strict allowlist such as ^[A-Za-z0-9_]{1,63}$ before calling listen/3.
- Map untrusted identifiers to safe internal channel names using a lookup table or deterministic hash instead of passing raw input.
- Isolate notification connections per tenant so a corrupted replay query cannot affect notifications for other tenants.
# Update postgrex to the patched release
mix deps.update postgrex
grep '"postgrex"' mix.lock
# Expected: {:hex, :postgrex, "0.22.3", ...}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

