CVE-2026-53426 Overview
CVE-2026-53426 is an unbounded resource allocation flaw in the leandrocp/mdex Elixir library. The MDEx.parse_document/2 function accepts a {:json, json} source and passes attacker-controlled node_type values through Module.concat/1, which invokes String.to_atom/1. Because atoms on the BEAM virtual machine are never garbage collected, a crafted JSON document with unique node_type values at every nested node interns a permanent atom per node. A single request can exhaust the default atom table of roughly 1,048,576 entries and abort the entire Erlang VM. The flaw affects mdex versions from 0.4.3 before 0.13.2 and is classified as [CWE-770].
Critical Impact
Any application passing untrusted input to MDEx.parse_document with a {:json, ...} source is exposed to an unauthenticated denial-of-service that terminates every process on the Erlang node.
Affected Products
- leandrocp/mdex Elixir library versions 0.4.3 through 0.13.1
- Elixir and Erlang/BEAM applications that pass untrusted JSON to MDEx.parse_document/2
- Phoenix and other Elixir web services integrating MDEx for Markdown rendering from JSON input
Discovery Timeline
- 2026-06-29 - CVE-2026-53426 published to NVD
- 2026-06-30 - Last updated in NVD database
Technical Details for CVE-2026-53426
Vulnerability Analysis
The vulnerability resides in the private json_to_node/1 function in lib/mdex.ex. When MDEx.parse_document/2 receives a {:json, json} tuple, the parser recursively walks the JSON document and extracts the node_type field from each object. That value is fed directly into Module.concat/1, which internally calls String.to_atom/1 to create a module reference.
Atoms are a fundamental primitive of the Erlang runtime and are stored in a global, non-reclaimable table. The default table size is approximately 1,048,576 entries. Once the limit is reached, the BEAM emergency-aborts with system_limit, killing every supervised process on the node.
Because the parser walks arbitrarily nested JSON, a compact document containing hundreds of thousands of unique node_type strings interns one atom per node. Repeated requests amplify the effect until the atom table is saturated.
Root Cause
The root cause is the use of Module.concat/1 on untrusted input. That function unconditionally creates new atoms and provides no safeguards. Elixir provides Module.safe_concat/1, which only resolves atoms that already exist and raises when an unknown value is supplied. The vulnerable code path failed to use the safe variant.
Attack Vector
An attacker submits a JSON document to any endpoint that forwards user input into MDEx.parse_document(source, format: :json). The document contains many nested objects, each with a distinct node_type string such as "nt_00001", "nt_00002", and so on. No authentication is required, and each request permanently consumes atom-table capacity until the node crashes.
// Security patch in lib/mdex.ex - json_to_node/1
defp json_to_node(json) do
{node_type, node} = Map.pop!(json, :node_type)
- node_type = Module.concat([node_type])
+ node_type = Module.safe_concat([node_type])
node = map_nodes(node)
struct(node_type, node)
end
Source: GitHub Commit 00fddf4. The fix swaps Module.concat/1 for Module.safe_concat/1, forcing the parser to reject node_type values that do not map to a pre-existing module atom.
Detection Methods for CVE-2026-53426
Indicators of Compromise
- Sudden growth in the Erlang atom table observable via :erlang.system_info(:atom_count) approaching the :atom_limit value.
- BEAM crash reports containing system_limit errors referencing atom creation.
- Repeated HTTP requests posting JSON payloads with high-cardinality, machine-generated node_type string values to Markdown-processing endpoints.
Detection Strategies
- Instrument application telemetry to poll :erlang.system_info(:atom_count) and alert when utilization exceeds a defined threshold (for example, 60 percent of :atom_limit).
- Inspect request bodies handed to MDEx.parse_document for JSON structures with abnormally deep nesting and unique node_type values across siblings.
- Correlate BEAM node restarts with request logs referencing MDEx JSON parsing routes to identify triggering payloads.
Monitoring Recommendations
- Ship BEAM VM metrics (atom count, process count, memory) to a centralized observability platform and set alerts on rapid atom-table growth.
- Log the size and depth of JSON documents accepted by MDEx endpoints and flag outliers for review.
- Track abnormal node terminations and supervisor restart storms that could indicate atom-table exhaustion attacks.
How to Mitigate CVE-2026-53426
Immediate Actions Required
- Upgrade mdex to version 0.13.2 or later in every Elixir application dependency graph.
- Audit all call sites of MDEx.parse_document/2 and confirm whether they accept untrusted {:json, ...} input.
- Temporarily disable JSON-source Markdown parsing on internet-facing endpoints until the upgrade is verified.
Patch Information
The fix landed in commit 00fddf4 and shipped in mdex 0.13.2. It replaces Module.concat/1 with Module.safe_concat/1 in json_to_node/1, preventing new atom creation from untrusted node_type values. Refer to the GitHub Security Advisory GHSA-923r-7vf4-5vw8 and the CNA CVE-2026-53426 record for full advisory details.
Workarounds
- Reject or restrict the {:json, ...} source of MDEx.parse_document/2 when handling untrusted input; accept only the string Markdown source.
- Validate incoming JSON against a strict schema that constrains node_type to a small allowlist before invoking MDEx.
- Enforce request size and nesting-depth limits at the web layer to bound the number of node_type values processed per request.
# Update mix.exs and fetch the patched release
# mix.exs
# defp deps do
# [
# {:mdex, "~> 0.13.2"}
# ]
# end
mix deps.update mdex
mix deps.get
mix compile --force
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

