EEF-CVE-2026-117645
Dashboard / Vulnerabilities / EEF-CVE-2026-117645
Summary: Phoenix.HTML vulnerable to attribute injection via unvalidated attribute names in attributes_escape/1
Details: ## Summary Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') vulnerability in phoenixframework phoenix_html allows an attacker who controls attribute names to inject attributes and script into rendered HTML via `Phoenix.HTML.attributes_escape/1`. `attributes_escape/1` in `lib/phoenix_html.ex` escapes attribute values but not attribute names. `key_escape/1` passes a binary key through `html_escape`, which neutralizes quotes and angle brackets but not whitespace, so a key such as `x onmouseover` ends the attribute and starts a new one. An atom key is only dasherized, so any character passes, including quotes and `>`. Nested `data`, `aria` and `phx` keys take the same paths. An application is affected when it builds attribute keys from untrusted input and renders them through HEEx `<tag {@attrs}>`, `content_tag/3`, or a direct call. Before 3.0.0 the same code lived in `Phoenix.HTML.Tag`. This issue affects phoenix_html: from 1.0.0 before 3.3.5 and from 4.0.0 before 4.4.0. ## Details **1. Attribute rendering.** `Phoenix.HTML.attributes_escape/1` walks the key-value pairs through `build_attrs/1` and `nested_attrs/3` and emits each as `key_escape(k)="attr_escape(v)"`. Values are escaped. Names are not checked against the HTML attribute-name grammar. **2. Binary names.** `key_escape/1` sends a binary through `Phoenix.HTML.Engine.html_escape/1`, which replaces `&`, `<`, `>`, `"` and `'`. Whitespace passes. HTML uses whitespace to separate attributes, so `%{"x onmouseover" => "alert(1)"}` renders ` x onmouseover="alert(1)"`: a harmless `x` attribute followed by an attacker-chosen event handler. **3. Atom names.** `key_escape/1` converts an atom with `Atom.to_string/1` and replaces `_` with `-`. Nothing is escaped, so `[{:"x\" onclick=\"alert(1)", "y"}]` renders ` x" onclick="alert(1)="y"` and `aria: [{:"x\"><script>", "y"}]` renders ` aria-x"><script>="y"`, which leaves the tag entirely. This path needs the application to create atoms from untrusted input, for example `Jason.decode!(body, keys: :atoms)` or `String.to_atom/1` on a parameter. **4. Reachability.** HEEx compiles `<div {@attrs}>` to a call to `Phoenix.HTML.attributes_escape/1` through `Phoenix.LiveView.HTMLEngine` and `Phoenix.LiveView.TagEngine`. `tag/2` and `content_tag/3` in `Phoenix.HTML.Tag` (phoenix_html 1.x to 3.x) and in `PhoenixHTMLHelpers.Tag` (4.x) render attributes the same way. Versions before 3.0.0 emitted the dasherized name without any escaping at all. ## Proof of concept 1. Install a vulnerable release, for example `Mix.install([{:phoenix_html, "== 4.3.0"}])`. 2. Render a binary name containing a space: `Phoenix.HTML.attributes_escape(%{"x onmouseover" => "alert(1)"}) |> Phoenix.HTML.safe_to_string()` returns ` x onmouseover="alert(1)"`. 3. Render an atom name containing a quote: `Phoenix.HTML.attributes_escape([{:"x\" onclick=\"alert(1)", "y"}]) |> Phoenix.HTML.safe_to_string()` returns ` x" onclick="alert(1)="y"`. 4. Render a nested atom name: `Phoenix.HTML.attributes_escape(aria: [{:"x\"><script>", "y"}]) |> Phoenix.HTML.safe_to_string()` returns ` aria-x"><script>="y"`. 5. Place any of these maps in a HEEx template as `<div {@attrs}>` to deliver the injected attribute to a browser. ## Impact An attacker who can influence the attribute names an application renders can add event-handler attributes or close the tag and run script in the browser of every user who views the page. ## Workarounds Do not derive attribute names from untrusted input. Where dynamic attribute keys are needed, check each key against an allow list, or against a pattern such as `^[a-zA-Z][a-zA-Z0-9:._-]*$`, before passing the map to `attributes_escape/1` or a HEEx `{@attrs}` expression. Do not create atoms from request data.
References: https://cna.erlef.org/cves/CVE-2026-117645.html, https://github.com/phoenixframework/phoenix_html/commit/6ad3a1d7960891a468d54636054a98e11dfb5111, https://github.com/phoenixframework/phoenix_html/commit/d9e4e6b2d9be49e25fb6d6b4fdbb2008174f01a6, https://github.com/phoenixframework/phoenix_html/commit/a47dc8944a773b7e9273d9691dc6744ec793f1de, https://hex.pm/packages/phoenix_html
Affected packages
Package
Name: phoenix_html
Purl: pkg:hex/phoenix_html
Affected ranges
Type: SEMVER
Events:
