{"id":"CVE-2026-32687","aliases":["EEF-CVE-2026-32687","EUVD-2026-29477","GHSA-r73h-97w8-m54h"],"status":"published","title":"SQL injection via channel name in Postgrex.Notifications.listen/3 and unlisten/3","description":"Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in elixir-ecto postgrex ('Elixir.Postgrex.Notifications' module) allows SQL Injection.\n\nThe channel argument passed to 'Elixir.Postgrex.Notifications':listen/3 and 'Elixir.Postgrex.Notifications':unlisten/3 is interpolated directly into LISTEN \"...\" / UNLISTEN \"...\" SQL statements without escaping the \" character. An attacker who can influence the channel name can inject a \" to break out of the quoted identifier and append arbitrary SQL. Because the notifications connection uses the PostgreSQL simple query protocol, multi-statement payloads are accepted, allowing DDL and DML commands to be chained (e.g. ; DROP TABLE ...; --). The same unsanitized interpolation also occurs in handle_connect/1 when replaying LISTEN commands after a reconnect.\n\nThis vulnerability is associated with program file lib/postgrex/notifications.ex and program routines 'Elixir.Postgrex.Notifications':listen/3, 'Elixir.Postgrex.Notifications':unlisten/3, 'Elixir.Postgrex.Notifications':handle_connect/1.\n\nThis issue affects postgrex: from 0.16.0 before 0.22.2.","severity":{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00198,"percentile":0.09728,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-89"],"published":"2026-05-12T14:18:07Z","modified":"2026-10-28T02:30:32Z","fixes":[{"ecosystem":"Hex","package":"postgrex","fixed_in":["0.22.2"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Hex","package":"postgrex","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.16.0"},{"fixed":"0.22.2"}]}]},{"source":"osv","ecosystem":"Hex","package":"postgrex","ranges":[{"type":"SEMVER","events":[{"introduced":"0.16.0"},{"fixed":"0.22.2"}]}],"versions":["0.16.0","0.16.1","0.16.2","0.16.3","0.16.4","0.16.5","0.17.0","0.17.1","0.17.2","0.17.3","0.17.4","0.17.5","0.18.0","0.19.0","0.19.1","0.19.2","0.19.3","0.20.0","0.21.0","0.21.1","0.22.0","0.22.1"]},{"source":"osv","ecosystem":"Hex","package":"postgrex","ranges":[{"type":"SEMVER","events":[{"introduced":"0.16.0"},{"fixed":"0.22.2"}]}],"versions":["0.16.0","0.16.1","0.16.2","0.16.3","0.16.4","0.16.5","0.17.0","0.17.1","0.17.2","0.17.3","0.17.4","0.17.5","0.18.0","0.19.0","0.19.1","0.19.2","0.19.3","0.20.0","0.21.0","0.21.1","0.22.0","0.22.1"]}],"products":[{"vendor":"elixir-ecto","product":"postgrex"}],"references":[{"url":"https://github.com/elixir-ecto/ecto/security/advisories/GHSA-r73h-97w8-m54h","tags":["vendor-advisory","related","Vendor Advisory","advisory","web"]},{"url":"https://cna.erlef.org/cves/CVE-2026-32687.html","tags":["related","Third Party Advisory","web"]},{"url":"https://osv.dev/vulnerability/EEF-CVE-2026-32687","tags":["related","Third Party Advisory","web"]},{"url":"https://github.com/elixir-ecto/postgrex/commit/7cdedbd4316bb65f82e6a9a4f922c0ac491cb770","tags":["patch","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-32687","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-r73h-97w8-m54h"},{"url":"https://github.com","tags":["web"]},{"url":"https://repo.hex.pm","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/32xxx/CVE-2026-32687.json","tags":["advisory"]},{"url":"https://github.com/elixir-ecto/postgrex.git","tags":["package"]},{"url":"https://hex.pm/packages/postgrex","tags":["package"]},{"url":"https://github.com/elixir-ecto/ecto","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-32687","title":"SQL injection via channel name in Postgrex.Notifications.listen/3 and unlisten/3","description":"Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in elixir-ecto postgrex ('Elixir.Postgrex.Notifications' module) allows SQL Injection.\n\nThe channel argument passed to 'Elixir.Postgrex.Notifications':listen/3 and 'Elixir.Postgrex.Notifications':unlisten/3 is interpolated directly into LISTEN \"...\" / UNLISTEN \"...\" SQL statements without escaping the \" character. An attacker who can influence the channel name can inject a \" to break out of the quoted identifier and append arbitrary SQL. Because the notifications connection uses the PostgreSQL simple query protocol, multi-statement payloads are accepted, allowing DDL and DML commands to be chained (e.g. ; DROP TABLE ...; --). The same unsanitized interpolation also occurs in handle_connect/1 when replaying LISTEN commands after a reconnect.\n\nThis vulnerability is associated with program file lib/postgrex/notifications.ex and program routines 'Elixir.Postgrex.Notifications':listen/3, 'Elixir.Postgrex.Notifications':unlisten/3, 'Elixir.Postgrex.Notifications':handle_connect/1.\n\nThis issue affects postgrex: from 0.16.0 before 0.22.2.","status":"published","published":"2026-05-12T14:18:07.607Z","modified":"2026-07-24T14:16:18.902Z","cvss":[{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"high","assessor":"cna"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T14:19:57.100857Z"}],"euvd":[{"id":"EUVD-2026-29477","description":"Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in elixir-ecto postgrex ('Elixir.Postgrex.Notifications' module) allows SQL Injection.\n\nThe channel argument passed to 'Elixir.Postgrex.Notifications':listen/3 and 'Elixir.Postgrex.Notifications':unlisten/3 is interpolated directly into LISTEN \"...\" / UNLISTEN \"...\" SQL statements without escaping the \" character. An attacker who can influence the channel name can inject a \" to break out of the quoted identifier and append arbitrary SQL. Because the notifications connection uses the PostgreSQL simple query protocol, multi-statement payloads are accepted, allowing DDL and DML commands to be chained (e.g. ; DROP TABLE ...; --). The same unsanitized interpolation also occurs in handle_connect/1 when replaying LISTEN commands after a reconnect.\n\nThis vulnerability is associated with program file lib/postgrex/notifications.ex and program routines 'Elixir.Postgrex.Notifications':listen/3, 'Elixir.Postgrex.Notifications':unlisten/3, 'Elixir.Postgrex.Notifications':handle_connect/1.\n\nThis issue affects postgrex: from 0.16.0 before 0.22.2.","status":"published","published":"2026-05-12T14:18:07Z","modified":"2026-07-24T14:16:18Z","cvss":[{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"high"}],"updated_at":"2026-11-02T18:19:40.671085Z"}],"ghsa":[{"id":"GHSA-r73h-97w8-m54h","title":"Postgrex: Channel-name SQL injection in `Postgrex.Notifications.listen/3`","description":"### Summary\n\nSQL injection in `Postgrex.Notifications.listen/3`: the `channel` argument is interpolated straight into `LISTEN \"...\"` / `UNLISTEN \"...\"` without escaping the `\"` character. Any caller that lets a user influence the channel name (e.g. a pub/sub bridge that uses a tenant id or topic slug as the channel) and the name is not sanitized can execute arbitrary SQL on the notifications connection.\n\nOnly those using the `Postgrex.Notifications` directly or via a dependency, with a non-sanitized channel name are vulnerable. Ecto does not use Postgrex.Notifications by default, but you must validate if any other dependency does.\n\n### Details\n\nPostgreSQL escapes a `\"` inside a quoted identifier by doubling it to `\"\"`. Postgrex doesn't do that doubling, so a `\"` inside `channel` closes the identifier early and everything after it is parsed as SQL on the same connection.\n\nThe notifications connection runs as whatever DB role the app is configured with. Unlike the `Postgrex.query/4` API, there's no extended-protocol guard here: `LISTEN`/`UNLISTEN` are sent as simple queries, so multi-statement payloads work and the attacker can chain `; CREATE TABLE …`, `; DROP …`, `; CREATE ROLE …`, etc.\n\n### Impact\n\nSQL injection on the notifications connection. Affects any application that calls `Postgrex.Notifications.listen/3` (or `unlisten/3`) with a channel name derived from untrusted input. Executes as the configured DB user with no protocol-level limit on what can be chained, so the realistic blast radius is read/modify/destroy on any data the app's DB role can reach.\n\n### Fix\n\nUpgrade to latest Postgrex v0.22.2 or later. Altenratively, sanitize any user input given as channel name by making sure it doesn't include quotes as well as null bytes (not strictly required, but recommended):\n\n```elixir\nif String.contains?(channel_name, [\"\\\"\", \"\\0\"]) do\n  raise \"bad channel name\"\nend\n```","status":"published","published":"2026-05-18T17:53:17Z","modified":"2026-05-18T17:53:19Z","cvss":[{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T14:40:11.298154Z"}],"nvd":[{"id":"CVE-2026-32687","description":"Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in elixir-ecto postgrex ('Elixir.Postgrex.Notifications' module) allows SQL Injection.\n\nThe channel argument passed to 'Elixir.Postgrex.Notifications':listen/3 and 'Elixir.Postgrex.Notifications':unlisten/3 is interpolated directly into LISTEN \"...\" / UNLISTEN \"...\" SQL statements without escaping the \" character. An attacker who can influence the channel name can inject a \" to break out of the quoted identifier and append arbitrary SQL. Because the notifications connection uses the PostgreSQL simple query protocol, multi-statement payloads are accepted, allowing DDL and DML commands to be chained (e.g. ; DROP TABLE ...; --). The same unsanitized interpolation also occurs in handle_connect/1 when replaying LISTEN commands after a reconnect.\n\nThis vulnerability is associated with program file lib/postgrex/notifications.ex and program routines 'Elixir.Postgrex.Notifications':listen/3, 'Elixir.Postgrex.Notifications':unlisten/3, 'Elixir.Postgrex.Notifications':handle_connect/1.\n\nThis issue affects postgrex: from 0.16.0 before 0.22.2.","status":"published","published":"2026-05-12T15:16:12.81Z","modified":"2026-07-24T15:17:16.537Z","cvss":[{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"},{"version":"3.1","score":7.8,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"primary"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T14:12:18.613259Z"}],"osv":[{"id":"CVE-2026-32687","title":"SQL injection via channel name in Postgrex.Notifications.listen/3 and unlisten/3","description":"Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in elixir-ecto postgrex ('Elixir.Postgrex.Notifications' module) allows SQL Injection.\n\nThe channel argument passed to 'Elixir.Postgrex.Notifications':listen/3 and 'Elixir.Postgrex.Notifications':unlisten/3 is interpolated directly into LISTEN \"...\" / UNLISTEN \"...\" SQL statements without escaping the \" character. An attacker who can influence the channel name can inject a \" to break out of the quoted identifier and append arbitrary SQL. Because the notifications connection uses the PostgreSQL simple query protocol, multi-statement payloads are accepted, allowing DDL and DML commands to be chained (e.g. ; DROP TABLE ...; --). The same unsanitized interpolation also occurs in handle_connect/1 when replaying LISTEN commands after a reconnect.\n\nThis vulnerability is associated with program file lib/postgrex/notifications.ex and program routines 'Elixir.Postgrex.Notifications':listen/3, 'Elixir.Postgrex.Notifications':unlisten/3, 'Elixir.Postgrex.Notifications':handle_connect/1.\n\nThis issue affects postgrex: from 0.16.0 before 0.22.2.","status":"published","published":"2026-05-12T14:18:07.607Z","modified":"2026-10-28T02:30:32.521162021Z","cvss":[{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T15:45:01.548715Z"},{"id":"EEF-CVE-2026-32687","title":"SQL injection via channel name in Postgrex.Notifications.listen/3 and unlisten/3","description":"## Summary\n\nImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in elixir-ecto postgrex ('Elixir.Postgrex.Notifications' module) allows SQL Injection.\n\nThe channel argument passed to 'Elixir.Postgrex.Notifications':listen/3 and 'Elixir.Postgrex.Notifications':unlisten/3 is interpolated directly into LISTEN \"...\" / UNLISTEN \"...\" SQL statements without escaping the \" character. An attacker who can influence the channel name can inject a \" to break out of the quoted identifier and append arbitrary SQL. Because the notifications connection uses the PostgreSQL simple query protocol, multi-statement payloads are accepted, allowing DDL and DML commands to be chained (e.g. ; DROP TABLE ...; --). The same unsanitized interpolation also occurs in handle\\_connect/1 when replaying LISTEN commands after a reconnect.\n\nThis vulnerability is associated with program file lib/postgrex/notifications.ex and program routines 'Elixir.Postgrex.Notifications':listen/3, 'Elixir.Postgrex.Notifications':unlisten/3, 'Elixir.Postgrex.Notifications':handle\\_connect/1.\n\nThis issue affects postgrex: from 0.16.0 before 0.22.2.\n\n## Configurations\n\nThe application must call 'Elixir.Postgrex.Notifications':listen/3 or 'Elixir.Postgrex.Notifications':unlisten/3 with a channel name derived from untrusted user input.","status":"published","published":"2026-05-12T14:18:07.607Z","modified":"2026-09-08T03:45:10.142984119Z","cvss":[{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T15:46:33.047032Z"},{"id":"GHSA-r73h-97w8-m54h","title":"Postgrex: Channel-name SQL injection in `Postgrex.Notifications.listen/3`","description":"### Summary\n\nSQL injection in `Postgrex.Notifications.listen/3`: the `channel` argument is interpolated straight into `LISTEN \"...\"` / `UNLISTEN \"...\"` without escaping the `\"` character. Any caller that lets a user influence the channel name (e.g. a pub/sub bridge that uses a tenant id or topic slug as the channel) and the name is not sanitized can execute arbitrary SQL on the notifications connection.\n\nOnly those using the `Postgrex.Notifications` directly or via a dependency, with a non-sanitized channel name are vulnerable. Ecto does not use Postgrex.Notifications by default, but you must validate if any other dependency does.\n\n### Details\n\nPostgreSQL escapes a `\"` inside a quoted identifier by doubling it to `\"\"`. Postgrex doesn't do that doubling, so a `\"` inside `channel` closes the identifier early and everything after it is parsed as SQL on the same connection.\n\nThe notifications connection runs as whatever DB role the app is configured with. Unlike the `Postgrex.query/4` API, there's no extended-protocol guard here: `LISTEN`/`UNLISTEN` are sent as simple queries, so multi-statement payloads work and the attacker can chain `; CREATE TABLE …`, `; DROP …`, `; CREATE ROLE …`, etc.\n\n### Impact\n\nSQL injection on the notifications connection. Affects any application that calls `Postgrex.Notifications.listen/3` (or `unlisten/3`) with a channel name derived from untrusted input. Executes as the configured DB user with no protocol-level limit on what can be chained, so the realistic blast radius is read/modify/destroy on any data the app's DB role can reach.\n\n### Fix\n\nUpgrade to latest Postgrex v0.22.2 or later. Altenratively, sanitize any user input given as channel name by making sure it doesn't include quotes as well as null bytes (not strictly required, but recommended):\n\n```elixir\nif String.contains?(channel_name, [\"\\\"\", \"\\0\"]) do\n  raise \"bad channel name\"\nend\n```","status":"published","published":"2026-05-18T17:53:17Z","modified":"2026-05-18T18:04:55.880624Z","cvss":[{"version":"4.0","score":7.5,"vector":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"high"}],"cwes":["CWE-89"],"updated_at":"2026-11-02T15:46:55.480549Z"}]}}
