{"id":"CVE-2026-56812","aliases":["EEF-CVE-2026-56812","EUVD-2026-42050","GHSA-63mc-hw7g-86rr"],"status":"published","title":"Phoenix JavaScript presence client crashes on presence keys colliding with Object.prototype members in Presence.syncState/syncDiff","description":"Improper Check for Unusual or Exceptional Conditions vulnerability in phoenixframework phoenix (Presence JavaScript client) allows an attacker with ordinary channel access to cause a persistent client-side denial of service against every viewer of a presence channel topic.\n\nThis vulnerability is associated with program files assets/js/phoenix/presence.js and program routines Presence.syncState and Presence.syncDiff.\n\nThe Phoenix JavaScript presence client checks whether a presence already exists with a bare truthiness test (state[key]) instead of an own-property check. Presence keys can be attacker-controlled, because applications track presences under a username or id supplied by the client. A user who joins a channel choosing a key that is an Object.prototype member name (__proto__, constructor, toString, hasOwnProperty, and similar) makes that lookup return JavaScript's built-in Object.prototype instead of undefined. Because the prototype is truthy, the code treats it as an existing presence and reads .metas.map(...) off it, which throws an uncaught TypeError.\n\nThe exception propagates out of the presence message handler, so the local state is never updated and onSync() never fires. Because the malicious key is tracked on the server, it is re-pushed on every presence update and keeps re-throwing, so presence sync stays broken for every viewer of that channel topic until the attacker leaves. Both syncState and syncDiff use the same unsafe existence-check pattern. The impact is limited to the affected topic and is a read-time confusion of the prototype object, not a mutation of Object.prototype (it is not prototype pollution).\n\nThis issue affects phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9; phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9.","severity":{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00796,"percentile":0.56949,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-754"],"published":"2026-07-07T15:22:46Z","modified":"2026-10-28T02:30:40Z","fixes":[{"ecosystem":"Hex","package":"phoenix","fixed_in":["1.5.15","1.6.17","1.7.24","1.8.9"],"sources":["ghsa","osv"]},{"ecosystem":"npm","package":"phoenix","fixed_in":["1.5.15","1.6.17","1.7.24","1.8.9"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.2.0-rc.0"},{"fixed":"1.5.15"}]}]},{"source":"ghsa","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.6.0-rc.0"},{"fixed":"1.6.17"}]}]},{"source":"ghsa","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.7.0-rc.0"},{"fixed":"1.7.24"}]}]},{"source":"ghsa","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.8.0-rc.0"},{"fixed":"1.8.9"}]}]},{"source":"ghsa","ecosystem":"npm","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.2.0-rc.0"},{"fixed":"1.5.15"}]}]},{"source":"ghsa","ecosystem":"npm","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.6.0-rc.0"},{"fixed":"1.6.17"}]}]},{"source":"ghsa","ecosystem":"npm","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.7.0-rc.0"},{"fixed":"1.7.24"}]}]},{"source":"ghsa","ecosystem":"npm","package":"phoenix","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.8.0-rc.0"},{"fixed":"1.8.9"}]}]},{"source":"osv","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.2.0-rc.0"},{"fixed":"1.5.15"}]},{"type":"SEMVER","events":[{"introduced":"1.6.0-rc.0"},{"fixed":"1.6.17"}]},{"type":"SEMVER","events":[{"introduced":"1.7.0-rc.0"},{"fixed":"1.7.24"}]},{"type":"SEMVER","events":[{"introduced":"1.8.0-rc.0"},{"fixed":"1.8.9"}]}],"versions":["1.2.0","1.2.0-rc.0","1.2.0-rc.1","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","1.3.0","1.3.0-rc.0","1.3.0-rc.1","1.3.0-rc.2","1.3.0-rc.3","1.3.1","1.3.2","1.3.3","1.3.4","1.3.5","1.4.0","1.4.0-rc.0","1.4.0-rc.1","1.4.0-rc.2","1.4.0-rc.3","1.4.1","1.4.10","1.4.11","1.4.12","1.4.13","1.4.14","1.4.15","1.4.16","1.4.17","1.4.18","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.4.9","1.5.0","1.5.0-rc.0","1.5.1","1.5.10","1.5.11","1.5.12","1.5.13","1.5.14","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.5.9","1.6.0","1.6.0-rc.0","1.6.0-rc.1","1.6.1","1.6.10","1.6.11","1.6.12","1.6.13","1.6.14","1.6.15","1.6.16","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.7.0","1.7.0-rc.0","1.7.0-rc.1","1.7.0-rc.2","1.7.0-rc.3","1.7.1","1.7.10","1.7.11","1.7.12","1.7.13","1.7.14","1.7.15","1.7.16","1.7.17","1.7.18","1.7.19","1.7.2","1.7.20","1.7.21","1.7.22","1.7.23","1.7.3","1.7.4","1.7.5","1.7.6","1.7.7","1.7.8","1.7.9","1.8.0","1.8.0-rc.0","1.8.0-rc.1","1.8.0-rc.2","1.8.0-rc.3","1.8.0-rc.4","1.8.1","1.8.2","1.8.3","1.8.4","1.8.5","1.8.6","1.8.7","1.8.8"]},{"source":"osv","ecosystem":"npm","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.2.0-rc.0"},{"fixed":"1.5.15"}]},{"type":"SEMVER","events":[{"introduced":"1.6.0-rc.0"},{"fixed":"1.6.17"}]},{"type":"SEMVER","events":[{"introduced":"1.7.0-rc.0"},{"fixed":"1.7.24"}]},{"type":"SEMVER","events":[{"introduced":"1.8.0-rc.0"},{"fixed":"1.8.9"}]}]},{"source":"osv","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.2.0-rc.0"},{"fixed":"1.5.15"}]}],"versions":["1.2.0","1.2.0-rc.0","1.2.0-rc.1","1.2.1","1.2.2","1.2.3","1.2.4","1.2.5","1.3.0","1.3.0-rc.0","1.3.0-rc.1","1.3.0-rc.2","1.3.0-rc.3","1.3.1","1.3.2","1.3.3","1.3.4","1.3.5","1.4.0","1.4.0-rc.0","1.4.0-rc.1","1.4.0-rc.2","1.4.0-rc.3","1.4.1","1.4.10","1.4.11","1.4.12","1.4.13","1.4.14","1.4.15","1.4.16","1.4.17","1.4.18","1.4.2","1.4.3","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.4.9","1.5.0","1.5.0-rc.0","1.5.1","1.5.10","1.5.11","1.5.12","1.5.13","1.5.14","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.5.9"]},{"source":"osv","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.6.0-rc.0"},{"fixed":"1.6.17"}]}],"versions":["1.6.0","1.6.0-rc.0","1.6.0-rc.1","1.6.1","1.6.10","1.6.11","1.6.12","1.6.13","1.6.14","1.6.15","1.6.16","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9"]},{"source":"osv","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.7.0-rc.0"},{"fixed":"1.7.24"}]}],"versions":["1.7.0","1.7.0-rc.0","1.7.0-rc.1","1.7.0-rc.2","1.7.0-rc.3","1.7.1","1.7.10","1.7.11","1.7.12","1.7.13","1.7.14","1.7.15","1.7.16","1.7.17","1.7.18","1.7.19","1.7.2","1.7.20","1.7.21","1.7.22","1.7.23","1.7.3","1.7.4","1.7.5","1.7.6","1.7.7","1.7.8","1.7.9"]},{"source":"osv","ecosystem":"Hex","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.8.0-rc.0"},{"fixed":"1.8.9"}]}],"versions":["1.8.0","1.8.0-rc.0","1.8.0-rc.1","1.8.0-rc.2","1.8.0-rc.3","1.8.0-rc.4","1.8.1","1.8.2","1.8.3","1.8.4","1.8.5","1.8.6","1.8.7","1.8.8"]},{"source":"osv","ecosystem":"npm","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.2.0-rc.0"},{"fixed":"1.5.15"}]}]},{"source":"osv","ecosystem":"npm","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.6.0-rc.0"},{"fixed":"1.6.17"}]}]},{"source":"osv","ecosystem":"npm","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.7.0-rc.0"},{"fixed":"1.7.24"}]}]},{"source":"osv","ecosystem":"npm","package":"phoenix","ranges":[{"type":"SEMVER","events":[{"introduced":"1.8.0-rc.0"},{"fixed":"1.8.9"}]}]}],"products":[{"vendor":"phoenixframework","product":"phoenix"}],"references":[{"url":"https://github.com/phoenixframework/phoenix/security/advisories/GHSA-63mc-hw7g-86rr","tags":["vendor-advisory","related","exploit","Exploit","Patch","Vendor Advisory","advisory","web"]},{"url":"https://cna.erlef.org/cves/CVE-2026-56812.html","tags":["related","Mitigation","Patch","Third Party Advisory","web"]},{"url":"https://osv.dev/vulnerability/EEF-CVE-2026-56812","tags":["related","Mitigation","Third Party Advisory","web"]},{"url":"https://github.com/phoenixframework/phoenix/commit/2270aaf21bd02c6a6a1022820564efb605a97655","tags":["related","fix","web"]},{"url":"https://github.com/phoenixframework/phoenix/commit/7f7b971c1ea0994e3fbd1c11ddb05e780bd38ad8","tags":["patch","Patch","fix","web"]},{"url":"https://github.com/phoenixframework/phoenix/commit/89a1c4be161e436241e12b2378a719904b9bd96f","tags":["patch","Patch","fix","web"]},{"url":"https://github.com/phoenixframework/phoenix/commit/b90b22521465ece00eb5a19d5aa2b9465b209c85","tags":["patch","Patch","fix","web"]},{"url":"https://github.com/phoenixframework/phoenix/commit/beffc4da1e787e572121f68902c63daf4fe7d9c2","tags":["patch","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-56812","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-63mc-hw7g-86rr"},{"url":"https://github.com","tags":["web"]},{"url":"https://registry.npmjs.org","tags":["web"]},{"url":"https://repo.hex.pm","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/56xxx/CVE-2026-56812.json","tags":["advisory"]},{"url":"https://github.com/phoenixframework/phoenix","tags":["package"]},{"url":"https://hex.pm/packages/phoenix","tags":["package"]},{"url":"https://www.npmjs.com/package/phoenix","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-56812","title":"Phoenix JavaScript presence client crashes on presence keys colliding with Object.prototype members in Presence.syncState/syncDiff","description":"Improper Check for Unusual or Exceptional Conditions vulnerability in phoenixframework phoenix (Presence JavaScript client) allows an attacker with ordinary channel access to cause a persistent client-side denial of service against every viewer of a presence channel topic.\n\nThis vulnerability is associated with program files assets/js/phoenix/presence.js and program routines Presence.syncState and Presence.syncDiff.\n\nThe Phoenix JavaScript presence client checks whether a presence already exists with a bare truthiness test (state[key]) instead of an own-property check. Presence keys can be attacker-controlled, because applications track presences under a username or id supplied by the client. A user who joins a channel choosing a key that is an Object.prototype member name (__proto__, constructor, toString, hasOwnProperty, and similar) makes that lookup return JavaScript's built-in Object.prototype instead of undefined. Because the prototype is truthy, the code treats it as an existing presence and reads .metas.map(...) off it, which throws an uncaught TypeError.\n\nThe exception propagates out of the presence message handler, so the local state is never updated and onSync() never fires. Because the malicious key is tracked on the server, it is re-pushed on every presence update and keeps re-throwing, so presence sync stays broken for every viewer of that channel topic until the attacker leaves. Both syncState and syncDiff use the same unsafe existence-check pattern. The impact is limited to the affected topic and is a read-time confusion of the prototype object, not a mutation of Object.prototype (it is not prototype pollution).\n\nThis issue affects phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9; phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9.","status":"published","published":"2026-07-07T15:22:46.933Z","modified":"2026-09-24T21:29:18.181Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"}],"cwes":["CWE-754"],"updated_at":"2026-11-02T13:37:02.780526Z"}],"euvd":[{"id":"EUVD-2026-42050","description":"Improper Check for Unusual or Exceptional Conditions vulnerability in phoenixframework phoenix (Presence JavaScript client) allows an attacker with ordinary channel access to cause a persistent client-side denial of service against every viewer of a presence channel topic.\n\nThis vulnerability is associated with program files assets/js/phoenix/presence.js and program routines Presence.syncState and Presence.syncDiff.\n\nThe Phoenix JavaScript presence client checks whether a presence already exists with a bare truthiness test (state[key]) instead of an own-property check. Presence keys can be attacker-controlled, because applications track presences under a username or id supplied by the client. A user who joins a channel choosing a key that is an Object.prototype member name (__proto__, constructor, toString, hasOwnProperty, and similar) makes that lookup return JavaScript's built-in Object.prototype instead of undefined. Because the prototype is truthy, the code treats it as an existing presence and reads .metas.map(...) off it, which throws an uncaught TypeError.\n\nThe exception propagates out of the presence message handler, so the local state is never updated and onSync() never fires. Because the malicious key is tracked on the server, it is re-pushed on every presence update and keeps re-throwing, so presence sync stays broken for every viewer of that channel topic until the attacker leaves. Both syncState and syncDiff use the same unsafe existence-check pattern. The impact is limited to the affected topic and is a read-time confusion of the prototype object, not a mutation of Object.prototype (it is not prototype pollution).\n\nThis issue affects phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9; phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9.","status":"published","published":"2026-07-07T15:22:46Z","modified":"2026-09-24T21:29:18Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T17:27:56.296478Z"}],"ghsa":[{"id":"GHSA-63mc-hw7g-86rr","title":"Phoenix: Presence keys colliding with `Object.prototype` members break existence checks","description":"### Summary\n\nThe Phoenix JavaScript presence client (`assets/js/phoenix/presence.js`) tests whether a presence already exists using a bare truthiness check (`state[key]`) rather than an own-property check. Because applications commonly track presences under a client-supplied username or id, the presence key can be attacker-controlled. A user who joins a channel and picks a key that names an `Object.prototype` member (`__proto__`, `constructor`, `toString`, `hasOwnProperty`, and similar) makes the lookup return the inherited `Object.prototype` object instead of `undefined`, which is truthy. The code then reads `.metas.map(...)` off it and throws an uncaught `TypeError`, breaking presence sync for every viewer of that channel topic. Any authenticated channel participant can trigger it.\n\n### Details\n\nThe victim is any browser subscribed to a presence channel. When it receives the server's `presence_state` message, it invokes `Presence.syncState`, which iterates the incoming presences and checks whether each one already exists locally via `let currentPresence = state[key]`. `state` is a plain object inheriting from `Object.prototype`. For an ordinary key like `alice`, `state[\"alice\"]` is `undefined` (falsy) and the safe path runs. For the key `__proto__` (or `constructor`, `toString`, etc.), `state[\"__proto__\"]` does not resolve to a tracked presence but to JavaScript's built-in `Object.prototype`, which is truthy. The `if(currentPresence)` guard passes, and the code evaluates `currentPresence.metas.map(m =\u003e m.phx_ref)`. Since `Object.prototype.metas` is `undefined`, calling `.map` on it throws a `TypeError`.\n\nPhoenix wraps no try/catch around channel binding callbacks, so the `TypeError` propagates out of the message handler: `this.state` is never updated and `onSync()` never fires. The malicious key is tracked server-side, so it is re-pushed on every presence update and keeps re-throwing, leaving presence permanently broken until the attacker leaves. `Presence.syncDiff` uses the same unsafe `state[key]` existence-check pattern, so presence diffs fail identically.\n\nTwo scoping points matter. The impact is per channel topic, not global: presence state is per-topic on the server and per-`Presence`-instance in the browser, so only viewers of the topic carrying the malicious key are affected. The bug is a read-time confusion of the prototype object, not prototype pollution: the crash occurs on the `state[\"__proto__\"]` read in `syncState`, before any `state[key] = ...` write is reached, so `Object.prototype` is never mutated and nothing leaks across channels. The fix builds the state and accumulator objects with `Object.create(null)` (or a `Map`) and gates existence checks with `Object.prototype.hasOwnProperty.call(obj, key)`.\n\nIf an application does not pass a client-controlled key to `Presence.track`, it is **not** affected.\n\n### PoC\n\n1. Connect to an application that uses `Phoenix.Presence` and tracks presences under a client-chosen key (e.g. a username).\n2. Join a presence channel choosing the key `__proto__` (or `constructor`, `toString`, `hasOwnProperty`).\n3. The server tracks the presence and pushes `presence_state` / `presence_diff` to every subscriber of that topic.\n4. Each viewer's `Presence.syncState` (or `syncDiff`) reads `state[\"__proto__\"]`, gets the truthy `Object.prototype`, and throws an uncaught `TypeError`.\n5. Presence sync stays broken for all viewers of the topic until the attacker leaves the channel.\n\n### Impact\n\nAn attacker with ordinary channel access can cause a persistent, stored client-side denial of service against every browser viewing a presence channel topic, freezing presence updates for all of them until the attacker disconnects. Any application driving the Phoenix JavaScript presence client with user-influenced presence keys is affected.","status":"published","published":"2026-09-03T20:30:33Z","modified":"2026-09-03T20:30:34Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-754"],"updated_at":"2026-11-02T14:41:11.621873Z"}],"nvd":[{"id":"CVE-2026-56812","description":"Improper Check for Unusual or Exceptional Conditions vulnerability in phoenixframework phoenix (Presence JavaScript client) allows an attacker with ordinary channel access to cause a persistent client-side denial of service against every viewer of a presence channel topic.\n\nThis vulnerability is associated with program files assets/js/phoenix/presence.js and program routines Presence.syncState and Presence.syncDiff.\n\nThe Phoenix JavaScript presence client checks whether a presence already exists with a bare truthiness test (state[key]) instead of an own-property check. Presence keys can be attacker-controlled, because applications track presences under a username or id supplied by the client. A user who joins a channel choosing a key that is an Object.prototype member name (__proto__, constructor, toString, hasOwnProperty, and similar) makes that lookup return JavaScript's built-in Object.prototype instead of undefined. Because the prototype is truthy, the code treats it as an existing presence and reads .metas.map(...) off it, which throws an uncaught TypeError.\n\nThe exception propagates out of the presence message handler, so the local state is never updated and onSync() never fires. Because the malicious key is tracked on the server, it is re-pushed on every presence update and keeps re-throwing, so presence sync stays broken for every viewer of that channel topic until the attacker leaves. Both syncState and syncDiff use the same unsafe existence-check pattern. The impact is limited to the affected topic and is a read-time confusion of the prototype object, not a mutation of Object.prototype (it is not prototype pollution).\n\nThis issue affects phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9; phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9.","status":"published","published":"2026-07-07T16:16:40.92Z","modified":"2026-10-25T20:10:00.147Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/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":"medium","assessor":"secondary"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"primary"}],"cwes":["CWE-754"],"updated_at":"2026-11-02T14:12:28.040501Z"}],"osv":[{"id":"CVE-2026-56812","title":"Phoenix JavaScript presence client crashes on presence keys colliding with Object.prototype members in Presence.syncState/syncDiff","description":"Improper Check for Unusual or Exceptional Conditions vulnerability in phoenixframework phoenix (Presence JavaScript client) allows an attacker with ordinary channel access to cause a persistent client-side denial of service against every viewer of a presence channel topic.\n\nThis vulnerability is associated with program files assets/js/phoenix/presence.js and program routines Presence.syncState and Presence.syncDiff.\n\nThe Phoenix JavaScript presence client checks whether a presence already exists with a bare truthiness test (state[key]) instead of an own-property check. Presence keys can be attacker-controlled, because applications track presences under a username or id supplied by the client. A user who joins a channel choosing a key that is an Object.prototype member name (__proto__, constructor, toString, hasOwnProperty, and similar) makes that lookup return JavaScript's built-in Object.prototype instead of undefined. Because the prototype is truthy, the code treats it as an existing presence and reads .metas.map(...) off it, which throws an uncaught TypeError.\n\nThe exception propagates out of the presence message handler, so the local state is never updated and onSync() never fires. Because the malicious key is tracked on the server, it is re-pushed on every presence update and keeps re-throwing, so presence sync stays broken for every viewer of that channel topic until the attacker leaves. Both syncState and syncDiff use the same unsafe existence-check pattern. The impact is limited to the affected topic and is a read-time confusion of the prototype object, not a mutation of Object.prototype (it is not prototype pollution).\n\nThis issue affects phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9; phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9.","status":"published","published":"2026-07-07T15:22:46.933Z","modified":"2026-10-28T02:30:40.100523334Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-754"],"updated_at":"2026-11-02T15:45:12.016252Z"},{"id":"EEF-CVE-2026-56812","title":"Phoenix JavaScript presence client crashes on presence keys colliding with Object.prototype members in Presence.syncState/syncDiff","description":"## Summary\n\nImproper Check for Unusual or Exceptional Conditions vulnerability in phoenixframework phoenix (Presence JavaScript client) allows an attacker with ordinary channel access to cause a persistent client-side denial of service against every viewer of a presence channel topic.\n\nThis vulnerability is associated with program files `assets/js/phoenix/presence.js` and program routines `Presence.syncState` and `Presence.syncDiff`.\n\nThe Phoenix JavaScript presence client checks whether a presence already exists with a bare truthiness test (`state[key]`) instead of an own-property check. Presence keys can be attacker-controlled, because applications track presences under a username or id supplied by the client. A user who joins a channel choosing a key that is an `Object.prototype` member name (`__proto__`, `constructor`, `toString`, `hasOwnProperty`, and similar) makes that lookup return JavaScript's built-in `Object.prototype` instead of `undefined`. Because the prototype is truthy, the code treats it as an existing presence and reads `.metas.map(...)` off it, which throws an uncaught `TypeError`.\n\nThe exception propagates out of the presence message handler, so the local state is never updated and `onSync()` never fires. Because the malicious key is tracked on the server, it is re-pushed on every presence update and keeps re-throwing, so presence sync stays broken for every viewer of that channel topic until the attacker leaves. Both `syncState` and `syncDiff` use the same unsafe existence-check pattern. The impact is limited to the affected topic and is a read-time confusion of the prototype object, not a mutation of `Object.prototype` (it is not prototype pollution).\n\nThis issue affects phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9; phoenix: from 1.2.0-rc.0 before 1.5.15, from 1.6.0-rc.0 before 1.6.17, from 1.7.0-rc.0 before 1.7.24, and from 1.8.0-rc.0 before 1.8.9.\n\n## Workarounds\n\nReject or sanitize presence keys that collide with JavaScript `Object.prototype` member names (for example `__proto__`, `constructor`, `prototype`, `toString`, `hasOwnProperty`) before calling `Phoenix.Presence.track`, or namespace every presence key with a fixed prefix so that no key can equal a prototype member name. Alternatively, derive presence keys from server-controlled, validated values instead of raw user input.\n\n## Configurations\n\nThe application must use `Phoenix.Presence` (or otherwise drive the Phoenix JavaScript presence client) and track presences under keys that are influenced by untrusted client input, for example a username or id chosen by the connecting user. Applications that derive presence keys exclusively from server-controlled, validated values are not affected.","status":"published","published":"2026-07-07T15:22:46.933Z","modified":"2026-09-24T21:30:06.586470584Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-754"],"updated_at":"2026-11-02T15:46:33.520522Z"},{"id":"GHSA-63mc-hw7g-86rr","title":"Phoenix: Presence keys colliding with `Object.prototype` members break existence checks","description":"### Summary\n\nThe Phoenix JavaScript presence client (`assets/js/phoenix/presence.js`) tests whether a presence already exists using a bare truthiness check (`state[key]`) rather than an own-property check. Because applications commonly track presences under a client-supplied username or id, the presence key can be attacker-controlled. A user who joins a channel and picks a key that names an `Object.prototype` member (`__proto__`, `constructor`, `toString`, `hasOwnProperty`, and similar) makes the lookup return the inherited `Object.prototype` object instead of `undefined`, which is truthy. The code then reads `.metas.map(...)` off it and throws an uncaught `TypeError`, breaking presence sync for every viewer of that channel topic. Any authenticated channel participant can trigger it.\n\n### Details\n\nThe victim is any browser subscribed to a presence channel. When it receives the server's `presence_state` message, it invokes `Presence.syncState`, which iterates the incoming presences and checks whether each one already exists locally via `let currentPresence = state[key]`. `state` is a plain object inheriting from `Object.prototype`. For an ordinary key like `alice`, `state[\"alice\"]` is `undefined` (falsy) and the safe path runs. For the key `__proto__` (or `constructor`, `toString`, etc.), `state[\"__proto__\"]` does not resolve to a tracked presence but to JavaScript's built-in `Object.prototype`, which is truthy. The `if(currentPresence)` guard passes, and the code evaluates `currentPresence.metas.map(m =\u003e m.phx_ref)`. Since `Object.prototype.metas` is `undefined`, calling `.map` on it throws a `TypeError`.\n\nPhoenix wraps no try/catch around channel binding callbacks, so the `TypeError` propagates out of the message handler: `this.state` is never updated and `onSync()` never fires. The malicious key is tracked server-side, so it is re-pushed on every presence update and keeps re-throwing, leaving presence permanently broken until the attacker leaves. `Presence.syncDiff` uses the same unsafe `state[key]` existence-check pattern, so presence diffs fail identically.\n\nTwo scoping points matter. The impact is per channel topic, not global: presence state is per-topic on the server and per-`Presence`-instance in the browser, so only viewers of the topic carrying the malicious key are affected. The bug is a read-time confusion of the prototype object, not prototype pollution: the crash occurs on the `state[\"__proto__\"]` read in `syncState`, before any `state[key] = ...` write is reached, so `Object.prototype` is never mutated and nothing leaks across channels. The fix builds the state and accumulator objects with `Object.create(null)` (or a `Map`) and gates existence checks with `Object.prototype.hasOwnProperty.call(obj, key)`.\n\nIf an application does not pass a client-controlled key to `Presence.track`, it is **not** affected.\n\n### PoC\n\n1. Connect to an application that uses `Phoenix.Presence` and tracks presences under a client-chosen key (e.g. a username).\n2. Join a presence channel choosing the key `__proto__` (or `constructor`, `toString`, `hasOwnProperty`).\n3. The server tracks the presence and pushes `presence_state` / `presence_diff` to every subscriber of that topic.\n4. Each viewer's `Presence.syncState` (or `syncDiff`) reads `state[\"__proto__\"]`, gets the truthy `Object.prototype`, and throws an uncaught `TypeError`.\n5. Presence sync stays broken for all viewers of the topic until the attacker leaves the channel.\n\n### Impact\n\nAn attacker with ordinary channel access can cause a persistent, stored client-side denial of service against every browser viewing a presence channel topic, freezing presence updates for all of them until the attacker disconnects. Any application driving the Phoenix JavaScript presence client with user-influenced presence keys is affected.","status":"published","published":"2026-09-03T20:30:33Z","modified":"2026-09-03T20:45:04.985092212Z","cvss":[{"version":"4.0","score":6.3,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-754"],"updated_at":"2026-11-02T15:46:38.911014Z"}]}}
