{
  "affected": [
    {
      "database_specific": {
        "source": "https://cna.erlef.org/osv/EEF-CVE-2026-117645.json"
      },
      "package": {
        "ecosystem": "Hex",
        "name": "phoenix_html",
        "purl": "pkg:hex/phoenix_html"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.0.0"
            },
            {
              "fixed": "3.3.5"
            }
          ],
          "type": "SEMVER"
        },
        {
          "events": [
            {
              "introduced": "4.0.0"
            },
            {
              "fixed": "4.4.0"
            }
          ],
          "type": "SEMVER"
        }
      ],
      "versions": [
        "1.0.0",
        "1.0.1",
        "1.1.0",
        "1.2.0",
        "1.2.1",
        "1.3.0",
        "1.4.0",
        "2.0.0",
        "2.0.0-dev",
        "2.0.1",
        "2.1.0",
        "2.1.1",
        "2.1.2",
        "2.10.0",
        "2.10.1",
        "2.10.2",
        "2.10.3",
        "2.10.4",
        "2.10.5",
        "2.11.0",
        "2.11.1",
        "2.11.2",
        "2.12.0",
        "2.13.0",
        "2.13.1",
        "2.13.2",
        "2.13.3",
        "2.13.4",
        "2.14.0",
        "2.14.1",
        "2.14.2",
        "2.14.3",
        "2.2.0",
        "2.3.0",
        "2.3.1",
        "2.4.0",
        "2.4.0-dev",
        "2.5.0",
        "2.5.1",
        "2.6.0",
        "2.6.1",
        "2.6.2",
        "2.7.0",
        "2.7.0-dev",
        "2.8.0",
        "2.9.0",
        "2.9.1",
        "2.9.2",
        "2.9.3",
        "3.0.0",
        "3.0.1",
        "3.0.2",
        "3.0.3",
        "3.0.4",
        "3.1.0",
        "3.2.0",
        "3.3.0",
        "3.3.1",
        "3.3.2",
        "3.3.3",
        "3.3.4",
        "4.0.0",
        "4.1.0",
        "4.1.1",
        "4.2.0",
        "4.2.1",
        "4.3.0"
      ]
    },
    {
      "database_specific": {
        "source": "https://cna.erlef.org/osv/EEF-CVE-2026-117645.json"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6ad3a1d7960891a468d54636054a98e11dfb5111"
            },
            {
              "fixed": "d9e4e6b2d9be49e25fb6d6b4fdbb2008174f01a6"
            },
            {
              "fixed": "a47dc8944a773b7e9273d9691dc6744ec793f1de"
            }
          ],
          "repo": "https://github.com/phoenixframework/phoenix_html",
          "type": "GIT"
        }
      ],
      "versions": [
        "v3.3.4",
        "v4.3.0",
        "v4.2.1",
        "v4.2.0",
        "v3.3.3",
        "v4.1.1",
        "v4.1.0",
        "v4.0.0",
        "v3.3.2",
        "v3.3.1",
        "v3.3.0",
        "v3.1.0",
        "v3.0.4",
        "v3.0.3",
        "v3.0.2",
        "v3.0.0",
        "v2.14.2",
        "v2.14.1",
        "v.2.14.1",
        "v2.14.0",
        "v2.13.3",
        "v2.13.2",
        "v2.13.1",
        "v2.13.0",
        "v2.12.0",
        "v2.11.2",
        "v2.11.1",
        "v2.11.0",
        "v2.10.4",
        "v2.10.2",
        "v2.10.1",
        "v2.10.0",
        "v2.9.3",
        "v2.9.2",
        "v2.9.1",
        "v2.9.0",
        "v2.8.0",
        "v2.7.0",
        "v2.6.0",
        "v2.5.1",
        "v2.5.0",
        "v2.4.0",
        "v2.3.1",
        "v2.3.0",
        "v2.2.1",
        "v2.2.0",
        "v2.1.2",
        "v2.1.0",
        "v2.0.0",
        "v1.4.0",
        "v1.3.0",
        "v1.2.1",
        "v1.2.0",
        "v1.1.0",
        "v1.0.1",
        "v1.0.0"
      ]
    }
  ],
  "aliases": [
    "CVE-2026-117645"
  ],
  "credits": [
    {
      "name": "Andrew Moore",
      "type": "FINDER"
    },
    {
      "name": "Andrew Moore",
      "type": "REPORTER"
    },
    {
      "name": "Steffen Deusch",
      "type": "REMEDIATION_DEVELOPER"
    },
    {
      "name": "Jonatan Männchen / EEF",
      "type": "COORDINATOR"
    }
  ],
  "database_specific": {
    "capec_ids": [
      "CAPEC-63"
    ],
    "cpe_ids": [
      "cpe:2.3:a:phoenixframework:phoenix_html:*:*:*:*:*:*:*:*"
    ],
    "cwe_ids": [
      "CWE-79"
    ]
  },
  "details": "## Summary\n\nImproper 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`.\n\n`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`.\n\nThis issue affects phoenix_html: from 1.0.0 before 3.3.5 and from 4.0.0 before 4.4.0.\n\n## Details\n\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n## Proof of concept\n\n1. Install a vulnerable release, for example `Mix.install([{:phoenix_html, \"== 4.3.0\"}])`.\n2. 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)\"`.\n3. 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\"`.\n4. Render a nested atom name: `Phoenix.HTML.attributes_escape(aria: [{:\"x\\\"><script>\", \"y\"}]) |> Phoenix.HTML.safe_to_string()` returns ` aria-x\"><script>=\"y\"`.\n5. Place any of these maps in a HEEx template as `<div {@attrs}>` to deliver the injected attribute to a browser.\n\n## Impact\n\nAn 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.\n\n## Workarounds\n\nDo 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.",
  "id": "EEF-CVE-2026-117645",
  "modified": "2026-10-30T20:30:03.351618265Z",
  "published": "2026-10-30T20:25:06.758Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://cna.erlef.org/cves/CVE-2026-117645.html"
    },
    {
      "type": "WEB",
      "url": "https://github.com/phoenixframework/phoenix_html/commit/6ad3a1d7960891a468d54636054a98e11dfb5111"
    },
    {
      "type": "FIX",
      "url": "https://github.com/phoenixframework/phoenix_html/commit/d9e4e6b2d9be49e25fb6d6b4fdbb2008174f01a6"
    },
    {
      "type": "FIX",
      "url": "https://github.com/phoenixframework/phoenix_html/commit/a47dc8944a773b7e9273d9691dc6744ec793f1de"
    },
    {
      "type": "PACKAGE",
      "url": "https://hex.pm/packages/phoenix_html"
    }
  ],
  "schema_version": "1.9.2",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Phoenix.HTML vulnerable to attribute injection via unvalidated attribute names in attributes_escape/1"
}