CVE-2026-43967: Root-Cause Fix for Absinthe Fragment Validation DoS
Summary
Absinthe’s UniqueFragmentNames validation previously performed a full scan of all fragments for each fragment, yielding O(N²) behavior on attacker-controlled GraphQL documents. The patch replaces repeated counting with a single precomputed frequency map and adds regression coverage for duplicate handling and large unique-fragment inputs. This is a root-cause performance fix for the documented CPU exhaustion vector in fragment validation.
Analysis
Vulnerability
CVE-2026-43967 describes a denial-of-service condition in Absinthe’s GraphQL fragment validation where duplicate-name checking for fragments had quadratic time complexity. The vulnerable implementation iterated over every fragment and, for each one, executed Enum.count/2 across the full fragment list to determine whether the name appeared more than once. Because fragment definitions are fully attacker-controlled within a request document, a remote client could submit a document with many fragments and force excessive CPU consumption during validation. The advisory and CVE records identify this as an algorithmic complexity issue fixed in v1.10.2; see GHSA-9MHV-8H52-Q7Q2, MITRE, and NVD.
defp duplicate?(fragments, fragment) do
Enum.count(fragments, &(&1.name == fragment.name)) > 1
endThat helper was called once per fragment, so the total work scaled roughly with the square of the number of fragments. The included regression commentary in the patch test explicitly states that a single crafted request could stall a worker for seconds, which is consistent with the reported CPU exhaustion impact in the advisory at GitHub Advisory Database.
Patch
The patch in commit 223600c520493dcaf95080af552c413099f92c9d removes the per-fragment full-list scan and replaces it with a single pass that computes fragment-name frequencies using Enum.frequencies_by/2. Validation then becomes a simple map lookup per fragment. This changes the dominant cost from repeated linear scans to one linear preprocessing pass plus one linear mapping pass.
counts = Enum.frequencies_by(input.fragments, & &1.name)
fragments =
Enum.map(input.fragments, fn fragment ->
if Map.fetch!(counts, fragment.name) > 1 do
fragment
|> flag_invalid(:duplicate_name)
|> put_error(error(fragment))
else
fragment
end
end)
{:ok, %{input | fragments: fragments}}The test suite additions in the same commit strengthen both semantic and security coverage. Functional tests verify no-fragment, single-fragment, unique-fragment, duplicate-fragment, and mixed cases. A dedicated regression test constructs 5,000 unique fragments, runs the parse/blueprint pipeline, times the validation phase, and asserts completion under a threshold intended to detect reintroduction of quadratic behavior. These changes are directly visible in the commit at the upstream patch.
Review
Pros
- The patch addresses the actual complexity hotspot rather than masking symptoms. Precomputing frequencies eliminates the repeated
Enum.count/2scans and reduces the algorithm to linear work over the fragment list. - Behavioral equivalence is preserved for duplicate detection semantics: fragments with names occurring more than once are still flagged invalid and receive the same error path.
- The implementation is small, localized, and low-risk. It modifies only the validation phase logic for unique fragment names and does not introduce protocol or API changes.
- Regression coverage is materially improved. The new tests validate correctness across common duplicate scenarios and add an explicit performance-oriented security test tied to the reported issue.
- The patch is well aligned with the vulnerability description in GHSA-9MHV-8H52-Q7Q2 and NVD: CPU exhaustion via algorithmic complexity in fragment validation.
Cons
- The performance regression test uses a wall-clock threshold (
< 50msfor 5,000 fragments), which can be environment-sensitive in CI and may produce noise on slower runners. It is useful as a guardrail but not a formal complexity proof. - The fix is scoped to
UniqueFragmentNames. While appropriate for this CVE, it does not by itself guarantee that other validation phases are free of similar attacker-amplifiable O(N²) patterns. - The patch does not add explicit request-level resource controls such as fragment count limits, validation budgets, or timeouts. Those are defense-in-depth measures rather than root-cause remediation, but they remain relevant for operational hardening.
Verdict
Root-cause.
This patch directly removes the quadratic duplicate-checking pattern that enabled the denial-of-service condition and replaces it with a linear-time strategy based on precomputed name frequencies. The change is technically appropriate, minimal, and supported by both correctness tests and a targeted regression test for algorithmic complexity. For this specific vulnerability in fragment-name validation, the remediation appears complete and source-grounded by the upstream commit 223600c520493dcaf95080af552c413099f92c9d and the associated advisory GHSA-9MHV-8H52-Q7Q2.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- DoS GraphQL.ts
Best match for CVE-2026-43967 because it is a hands-on GraphQL-focused DoS lab tied to CWE-400 and covers defensive patterns for preventing resource-exhaustion issues in request processing and validation paths.
- DoS.api
Useful follow-on lab for understanding API-layer denial-of-service defenses such as request validation limits, complexity controls, and safe handling of attacker-controlled inputs that can amplify backend CPU usage.
- DoS.java
Good complementary lab for practicing general server-side DoS mitigations that transfer well to the Absinthe patch review context, including bounded processing, safer request handling, and defense-in-depth against CPU exhaustion.