CVE-2026-43967 Overview
CVE-2026-43967 is an algorithmic complexity vulnerability in the Absinthe GraphQL library for Elixir. The Elixir.Absinthe.Phase.Document.Validation.UniqueFragmentNames phase validates fragment-name uniqueness using a nested linear scan, producing O(N²) comparisons per document. Because the fragment list is built directly from attacker-supplied GraphQL query bodies, an unauthenticated remote attacker can submit a single ~1 MB request containing tens of thousands of fragment definitions and force billions of comparisons inside one validation pass. The defect affects Absinthe versions from 1.2.0 before 1.10.2 and is categorized under [CWE-407] Inefficient Algorithmic Complexity.
Critical Impact
A single unauthenticated HTTP request can exhaust CPU resources on any Elixir/Phoenix service exposing an Absinthe-backed GraphQL endpoint, producing a denial-of-service condition without schema knowledge or special configuration.
Affected Products
- absinthe-graphql/absinthe versions >= 1.2.0 and < 1.10.2
- Elixir applications using the Absinthe GraphQL toolkit for query parsing and validation
- Phoenix and Plug-based services exposing public GraphQL endpoints backed by vulnerable Absinthe releases
Discovery Timeline
- 2026-05-08 - CVE-2026-43967 published to NVD
- 2026-05-13 - Last updated in NVD database
Technical Details for CVE-2026-43967
Vulnerability Analysis
The vulnerability lives in the document validation pipeline that Absinthe runs against every parsed GraphQL operation. The run/2 function in lib/absinthe/phase/document/validation/unique_fragment_names.ex iterates over input.fragments and, for each fragment, calls a duplicate?/2 helper. That helper evaluates Enum.count(fragments, &(&1.name == name)), performing a full linear scan of the fragment list for every fragment processed.
The combined behavior yields O(N²) comparisons per document, where N is the count of fragment definitions in the request. A minimum-size fragment definition is approximately 16 bytes, so a ~1 MB request body carries roughly 60,000 fragments and forces about 3.6 × 10⁹ comparisons inside one validation phase. The work executes on the BEAM scheduler that handles the request and starves other requests on the same node.
Root Cause
The root cause is the use of Enum.count/2 inside a per-element loop instead of a single-pass frequency map. The validator treats each fragment lookup as independent, so an attacker-controlled input size multiplies cost quadratically with no upper bound applied prior to validation.
Attack Vector
Exploitation requires only network access to a GraphQL endpoint that uses Absinthe. The attacker sends a single POST request containing a query body populated with thousands of minimal fragment definitions. No authentication, schema introspection, or specific operation type is required. Repeated submissions can keep request-handling processes pinned at full CPU and prevent legitimate traffic from being serviced.
// Source: https://github.com/absinthe-graphql/absinthe/commit/223600c520493dcaf95080af552c413099f92c9d
// Patch in lib/absinthe/phase/document/validation/unique_fragment_names.ex
@spec run(Blueprint.t(), Keyword.t()) :: Phase.result_t()
def run(input, _options \\ []) do
- fragments =
- for fragment <- input.fragments do
- process(fragment, input.fragments)
- end
-
- result = %{input | fragments: fragments}
- {:ok, result}
- end
+ counts = Enum.frequencies_by(input.fragments, & &1.name)
- defp process(fragment, fragments) do
- if duplicate?(fragments, fragment) do
- fragment
- |> flag_invalid(:duplicate_name)
- |> put_error(error(fragment))
- else
- fragment
- end
- end
-
- defp duplicate?(fragments, fragment) do
- Enum.count(fragments, &(&1.name == fragment.name)) > 1
The fix replaces the nested scan with a single call to Enum.frequencies_by/2, reducing the cost of uniqueness validation from O(N²) to O(N).
Detection Methods for CVE-2026-43967
Indicators of Compromise
- GraphQL POST requests with abnormally large bodies (hundreds of kilobytes to several megabytes) consisting primarily of fragment keyword tokens.
- Sustained CPU saturation on BEAM processes handling GraphQL traffic, often accompanied by long Absinthe pipeline execution times.
- Spikes in HTTP request latency or 504 Gateway Timeout responses originating from endpoints fronting Absinthe services.
Detection Strategies
- Inspect web access logs for GraphQL requests whose body size or fragment token count exceed normal application baselines.
- Instrument Absinthe with telemetry to record per-phase execution time, and alert when Absinthe.Phase.Document.Validation.UniqueFragmentNames durations exceed expected thresholds.
- Correlate process scheduler utilization on Erlang VM nodes with incoming GraphQL request volume to surface single-request CPU exhaustion patterns.
Monitoring Recommendations
- Track the dependency manifest (mix.exs, mix.lock) across services to identify any Absinthe version below 1.10.2.
- Add rate-limit and request-size metrics at the reverse proxy or API gateway for all GraphQL routes.
- Monitor for repeated requests from the same source IP that trigger long GraphQL parse or validation phases.
How to Mitigate CVE-2026-43967
Immediate Actions Required
- Upgrade Absinthe to version 1.10.2 or later in all affected applications and redeploy.
- Enforce a maximum request body size for GraphQL endpoints at the reverse proxy or Plug.Parsers layer.
- Apply a per-IP rate limit to GraphQL routes until patched builds are deployed across all environments.
Patch Information
The upstream fix replaces the quadratic scan with Enum.frequencies_by/2 and is included in Absinthe 1.10.2. Patch details are available in the GitHub commit and the GitHub Security Advisory GHSA-9mhv-8h52-q7q2. Additional context is published in the Erlang Ecosystem Foundation CNA advisory and the OSV report.
Workarounds
- Cap GraphQL request body size to a value well below 1 MB using Plug.Parsers:length option or an upstream proxy configuration.
- Reject queries containing more than a small ceiling of fragment definitions using a custom Absinthe pipeline phase that runs before UniqueFragmentNames.
- Require authentication on GraphQL endpoints where business requirements allow, reducing exposure to unauthenticated traffic.
# Example: pin Absinthe to a patched release in mix.exs
# {:absinthe, "~> 1.10.2"}
mix deps.update absinthe
mix deps.get
mix compile
# Example: enforce a body-size limit at the Plug parser layer
# In endpoint.ex
# plug Plug.Parsers,
# parsers: [:urlencoded, :multipart, :json],
# pass: ["*/*"],
# json_decoder: Jason,
# length: 262_144 # 256 KB cap for GraphQL payloads
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


