Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-48590

CVE-2026-48590: XmlBuilder Module XXE Vulnerability

CVE-2026-48590 is an XML injection flaw in the joshnuss xml_builder module that allows attackers to inject arbitrary XML markup through unvalidated element and attribute names. This post covers technical details, affected versions from 0.0.1 to 2.4.0, security impact, and available patches.

Updated:

CVE-2026-48590 Overview

CVE-2026-48590 is an XML Injection vulnerability [CWE-91] in the joshnuss/xml_builder Elixir library. The flaw resides in the XmlBuilder module, specifically in lib/xml_builder.ex and the routines XmlBuilder.generate/1, XmlBuilder.generate/2, XmlBuilder.element/1, XmlBuilder.element/2, and XmlBuilder.element/3. Element names, attribute names, and doctype identifiers are interpolated verbatim into serialized XML without validation or escaping of structural characters (<, >, ", ', &). An attacker who influences a name argument, for example one derived from a JSON key or HTTP form field name, can inject arbitrary XML markup into the resulting document. The issue affects xml_builder from version 0.0.1 before 2.4.1.

Critical Impact

Attackers can inject arbitrary XML elements, comments, and event-handler attributes into generated documents, enabling content spoofing and downstream XML injection when generated output is consumed by other parsers or rendered to users.

Affected Products

  • joshnuss/xml_builder Elixir library, versions 0.0.1 through 2.4.0
  • Applications embedding xml_builder that pass untrusted input into element or attribute name arguments
  • Downstream Elixir/Erlang services that consume XmlBuilder.generate/* output without secondary sanitization

Discovery Timeline

  • 2026-08-21 - CVE-2026-48590 published to NVD
  • 2026-08-24 - Last updated in NVD database

Technical Details for CVE-2026-48590

Vulnerability Analysis

The xml_builder library serializes Elixir tuples of the shape {name, attrs, content} into XML text. Before the fix, the format/3 clauses converted the name term directly with to_string/1 and concatenated it into the output stream. No structural character escaping was applied to element names, attribute names, or doctype identifiers.

When an application passes user-controlled data as an element or attribute name, for example XmlBuilder.element(user_supplied_key, %{}, value), the attacker controls raw bytes that the serializer emits between the < and > delimiters. This allows the attacker to break out of the intended tag, inject additional tags, insert comments, or add attributes such as event handlers.

The consequence is content spoofing in the produced XML and potential cross-context injection when the output is later parsed as HTML, SVG, RSS, or a signed SAML/SOAP payload. Because integrity of generated markup is broken while the producing process itself is not compromised, the impact is scoped to output integrity rather than confidentiality or availability of the host.

Root Cause

The root cause is missing output encoding of structural XML characters in name positions. The pre-patch code paths in lib/xml_builder.ex used to_string(name) inside the <...>, </...>, and attribute-key contexts. to_string/1 performs type conversion but no XML escaping, so characters such as <, >, ", ', and & were emitted literally. This maps to [CWE-91]: XML Injection (improper neutralization of data within XML).

Attack Vector

Exploitation requires that an attacker control input that reaches a name argument of XmlBuilder.element/* or the doctype identifier used by XmlBuilder.generate/*. Common paths include:

  • Serializing a Map to XML where map keys are derived from a JSON body or HTTP form field names.
  • Rendering XML feeds where element names are copied from a database column populated by user submissions.
  • Building doctype declarations from configuration or request parameters.

A payload such as foo><script>alert(1)</script><foo supplied as a name causes the serializer to emit unexpected tags. Injected event-handler attributes can alter parser interpretation in downstream consumers.

elixir
# Pre-patch behavior in lib/xml_builder.ex (excerpt from the fix commit)
defp format({name, attrs, content}, level, options)
     when is_blank_attrs(attrs) and is_blank_list(content),
-    do: [indent(level, options), ~c"<", to_string(name), ~c"/>"]
+    do: [indent(level, options), ~c"<", sanitize!(name), ~c"/>"]

defp format({name, attrs, content}, level, options) when is_blank_list(content),
-  do: [indent(level, options), ~c"<", to_string(name), ~c" ", format_attributes(attrs), ~c"/>"]
+  do: [indent(level, options), ~c"<", sanitize!(name), ~c" ", format_attributes(attrs), ~c"/>"]

defp format({name, attrs, content}, level, options)
     when is_blank_attrs(attrs) and not is_list(content),
     do: [
       indent(level, options),
       ~c"<",
-      to_string(name),
+      sanitize!(name),
       ~c">",
       format_content(content, level + 1, options),
       ~c"</",
-      to_string(name),
+      sanitize!(name),
       ~c">"
     ]

Source: GitHub Commit d5c0aec

The patch introduces a sanitize!/1 helper and a dedicated exception module. A SanitizationError is raised when a name contains disallowed structural characters:

elixir
# lib/xml_builder/sanitization_error.ex
defmodule XmlBuilder.SanitizationError do
  defexception [:message]
end

Source: GitHub Commit d5c0aec

Detection Methods for CVE-2026-48590

Indicators of Compromise

  • Generated XML documents that contain unexpected tags, comments, or attributes not present in the calling code's tag schema.
  • Runtime raises of XmlBuilder.SanitizationError after upgrading, indicating call sites that pass attacker-influenced names.
  • Log entries showing XmlBuilder.element/* invocations where the name argument originates from HTTP form fields, JSON keys, or database-backed user content.

Detection Strategies

  • Perform a dependency inventory across Elixir and Erlang projects and flag any mix.lock entry pinning xml_builder below 2.4.1.
  • Use static analysis or grep for calls to XmlBuilder.element, XmlBuilder.generate, and XmlBuilder.doctype where name arguments are not compile-time literals or atoms from a fixed allow list.
  • Add fuzz test cases that pass XML metacharacters as element and attribute names to affected code paths and verify the serialized output is well-formed.

Monitoring Recommendations

  • Monitor application error streams for XmlBuilder.SanitizationError exceptions post-upgrade and treat repeated occurrences from the same source as probable abuse.
  • Log and alert on outbound XML documents that fail schema validation at consumers, which can indicate injected structural characters.
  • Track versions of xml_builder deployed across build pipelines using Software Bill of Materials (SBOM) tooling to prevent regression to a vulnerable release.

How to Mitigate CVE-2026-48590

Immediate Actions Required

  • Upgrade xml_builder to version 2.4.1 or later in every affected project and rebuild release artifacts.
  • Audit call sites of XmlBuilder.element/*, XmlBuilder.generate/*, and doctype builders for name arguments sourced from untrusted input.
  • Refactor code so element and attribute names come from a fixed allow list of atoms rather than dynamic strings.

Patch Information

The fix is delivered in xml_builder2.4.1 via commit d5c0aec. It introduces a sanitize!/1 function invoked at every name emission site and adds XmlBuilder.SanitizationError to signal invalid inputs at runtime. See the GitHub Security Advisory GHSA-r82p-3q2p-728w and the Erlang Ecosystem CNA advisory for authoritative details. The OSV record EEF-CVE-2026-48590 provides machine-readable version ranges for automated scanning.

Workarounds

  • Where an immediate upgrade is not feasible, wrap all calls to XmlBuilder.element/* with a caller-side validator that rejects names containing <, >, ", ', &, or whitespace.
  • Restrict element and attribute names to atoms defined in the calling module, never derived from request-time data.
  • Validate downstream XML with a strict schema (XSD or Relax NG) at the consumer boundary to detect and reject injected markup.
bash
# Update the xml_builder dependency in mix.exs
# {:xml_builder, "~> 2.4.1"}

# Refresh the lockfile and fetch the fixed release
mix deps.update xml_builder
mix deps.get

# Verify the resolved version
mix deps | grep xml_builder

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.