May 14, 2026·6 min read·6 visits
A quadratic complexity flaw (O(N²)) in Absinthe's GraphQL fragment validation allows remote attackers to trigger severe CPU exhaustion via crafted requests, causing a Denial of Service. The vulnerability is patched in v1.10.2.
Absinthe, an Elixir GraphQL toolkit, is vulnerable to a Denial of Service (DoS) condition due to inefficient algorithmic complexity in its document validation phase. Unauthenticated attackers can exhaust server resources by submitting GraphQL requests with heavily duplicated fragment definitions.
Absinthe is a widely utilized GraphQL toolkit for the Elixir language, responsible for parsing, validating, and executing GraphQL documents. During the validation phase, the toolkit must ensure incoming queries comply with the GraphQL specification before execution. One specific requirement dictates that all fragment definitions within a document must possess unique names.
The vulnerability, identified as CVE-2026-43967, resides within the UniqueFragmentNames validation phase. This specific module implements an inefficient algorithm for verifying fragment name uniqueness, resulting in a CWE-407 (Inefficient Algorithmic Complexity) classification. The implementation relies on a nested loop structure that scales quadratically with the number of supplied fragments.
Remote unauthenticated attackers can leverage this inefficiency to trigger an application-layer Denial of Service (DoS). By submitting a single HTTP request containing an excessive number of minimal fragment definitions, an attacker forces the server into an intensive computational loop. This process monopolizes CPU resources and starves the Erlang VM schedulers, degrading the availability of the target application.
The fundamental flaw exists within the Absinthe.Phase.Document.Validation.UniqueFragmentNames:run/2 function. This function is tasked with processing a parsed GraphQL document representation, termed a Blueprint, and verifying the uniqueness of every fragment name. The original implementation achieved this by iterating over the list of fragments and calling a helper function to verify each entry.
For every fragment processed, the duplicate?/2 helper function utilized Enum.count/2 to scan the entire fragment list and count occurrences of the current fragment's name. This approach requires a full linear scan of the list for each individual element. Consequently, processing a document containing $N$ fragments results in $N \times N$ string comparison operations, representing an $O(N^2)$ time complexity.
In the Elixir and Erlang runtime environment, computationally expensive synchronous operations can disrupt the scheduler. While the BEAM VM utilizes preemptive scheduling based on reduction counts, a single process executing billions of string comparisons will pin its assigned scheduler thread. If an attacker issues multiple malicious requests concurrently, the worker pool becomes saturated, preventing the processing of legitimate traffic.
The vulnerable implementation explicitly processes each fragment sequentially against the entire fragment collection. The run/2 function maps over input.fragments, invoking process/2 for each item. The process/2 function then delegates to duplicate?/2, which executes Enum.count(fragments, &(&1.name == fragment.name)) > 1.
# Vulnerable Implementation
defp duplicate?(fragments, fragment) do
Enum.count(fragments, &(&1.name == fragment.name)) > 1
endThe patched implementation in commit 223600c520493dcaf95080af552c413099f92c9d eliminates the nested scan strategy. The fix introduces Enum.frequencies_by/2 to generate a frequency map of fragment names prior to validation. This built-in Elixir function iterates through the collection exactly once, constructing a hash map where keys are fragment names and values are their occurrence counts.
# Patched Implementation
def run(input, _options \\ []) do
counts = Enum.frequencies_by(input.fragments, & &1.name)
fragments = Enum.map(input.fragments, fn fragment ->
if Map.fetch!(counts, fragment.name) > 1 do
# ... flag error logic ...By utilizing a hash map for the occurrence data, the validation phase reduces the uniqueness check from an $O(N)$ list traversal to an $O(1)$ map lookup per fragment. The overall time complexity of the UniqueFragmentNames phase is successfully reduced from $O(N^2)$ to $O(N)$, mitigating the algorithmic bottleneck.
Exploitation of CVE-2026-43967 requires no authentication, special network positioning, or complex pre-conditions. The attacker merely needs the ability to route standard HTTP requests to the exposed GraphQL endpoint. The attack payload is constructed by packing thousands of syntactically valid but minimal fragment definitions into a single query.
Because a minimal fragment definition requires very few bytes (e.g., fragment f1 on T{a}), an attacker can bypass standard web server body size limits with ease. A standard 8MB body limit allows for hundreds of thousands of fragment definitions. A payload sized around 1MB containing 60,000 fragments will force the target server to execute approximately 3.6 billion string comparisons.
The official regression tests provide a localized Proof-of-Concept for this behavior. The test dynamically generates a string containing 5,000 unique fragments using Enum.map_join and concatenates them to a simple query. When executed against a vulnerable Absinthe version, the validation phase scales quadratically, requiring seconds to complete instead of the expected milliseconds.
# Local PoC demonstrating quadratic scaling
n = 5_000
fragments = Enum.map_join(1..n, " ", fn i -> "fragment f#{i} on Dog { name }" end)
document = "{ dog { name } } " <> fragmentsThe severity of this vulnerability is reflected in its CVSS 4.0 score of 8.7, driven entirely by the high impact on availability. Successful exploitation requires no elevated privileges and no user interaction. The attack operates entirely at the application layer, exploiting the logical complexity of the data processing pipeline rather than a memory corruption flaw.
When the malicious payload is processed, the affected Erlang process consumes significant CPU cycles. Due to the high number of string comparisons, the system experiences a prolonged period of high resource utilization. Multiple concurrent requests containing this payload will exhaust the available scheduler threads, rendering the application completely unresponsive to legitimate client requests.
Standard network defense mechanisms are generally ineffective against this specific vector without custom configuration. Web Application Firewalls (WAFs) typically do not parse GraphQL syntax to count fragment definitions. Furthermore, the malicious request does not exhibit standard signature-based indicators of compromise, as it relies entirely on valid, albeit redundant, syntax specified by the GraphQL standard.
The primary and most effective remediation strategy is upgrading the absinthe dependency to the patched version. Organizations utilizing affected versions (1.2.0 through 1.10.1) must update their mix.exs to require absinthe version 1.10.2 or later. This release contains the algorithmic optimization necessary to handle dense fragment payloads in linear time.
If immediate patching is not feasible, operators can implement compensating controls at the HTTP layer. Enforcing strict max_body_size limits within the web server configuration or the Phoenix endpoint can constrain the total number of fragments an attacker can supply. While this does not fix the root cause, it bounds the maximum computational time the vulnerability can consume.
Organizations should also consider implementing query complexity limits or maximum document size validations early in the request pipeline. Limiting the raw string length of the GraphQL input before it reaches the Absinthe parsing and validation phases provides a robust defense-in-depth measure against broad classes of algorithmic complexity attacks.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
absinthe absinthe-graphql | >= 1.2.0, < 1.10.2 | 1.10.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-407 |
| Attack Vector | Network |
| CVSS 4.0 Score | 8.7 |
| EPSS Score | 0.0016 |
| Impact | Denial of Service (Availability) |
| Exploit Status | PoC Available |
| CISA KEV | No |
The algorithm's performance scales poorly relative to input size, allowing attackers to consume excessive resources.