Repository navigation
chore(deps): update dependency graphql-core to v3.2.12 [security] - #102
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
3.2.11→3.2.12GraphQL-core: Denial of service via comment tokens bypassing the parser's max_tokens limit
CVE-2026-75508 / GHSA-r77w-qph3-7vf8
More information
Details
Summary
The parser's
max_tokenslimit — the documented defence against oversized GraphQLdocuments — did not count comment tokens. A document padded with comments could
therefore pass a
max_tokenscheck of any size while still allocating oneTokenobject per comment, all of which stay reachable for the lifetime of the parsed
document. A 38 MB comment-padded payload allocated roughly 92 MB of additional
resident memory with
max_tokens=5in force.Details
Lexer.lookahead()reads tokens one at a time and skips over comments, returning onlythe first non-comment token. Each skipped comment is nevertheless fully tokenized,
allocated as a
Tokenobject, and linked into the persistent token list viatoken.next/token.prev— the chain that powers source-location reporting, andwhich is retained as long as the document is.
Parser.advance_lexer()incremented the token counter only for the tokens the lexeractually returned. Comments were therefore invisible to the accounting, and
max_tokensbounded only the non-comment tokens.The practical consequence is a gap between what users reasonably believe
max_tokensprotects and what it did protect. The limit was not broken for ordinary documents —
real tokens were still capped correctly — so the shortfall was not observable without
specifically testing comment padding.
As with the companion issue, the payload compresses extremely well (
# c\nrepeated),so body-size limits and WAF inspection applied to the compressed request do not see
its true size, while the framework decompresses it in memory before the parser is
reached.
Proof of concept
Measured on GraphQL-core 3.2.8 / CPython 3.12 / Linux x86_64: the 391 KB payload
parsed in 127 ms, and the 38.1 MB payload produced an RSS delta of +92 MB — about
2.4× the source size, on top of the source string itself.
Impact
Denial of service through memory exhaustion. A modest number of concurrent requests
can push a worker past a typical 256 MB or 512 MB container limit and trigger an
OOM kill. No authentication is required, and no data is disclosed or modified.
Depth limiters and complexity analyzers do not help: they run on the AST after parsing
has already succeeded, and the document contains only five real tokens.
Patches
Fixed in 3.2.12 and 3.3.0rc1.
Parser.advance_lexernow counts every token consumed, including the comment tokensthe lexer skips between the previously focused token and the new one, and including
the advance onto EOF so that trailing comments are covered.
max_tokenstherefore nowbounds every
Tokenthe parser allocates.Note the behaviour change: comments now count toward
max_tokens. Acomment-heavy document that was previously accepted may now be rejected. If you parse
documents containing substantial comments — SDL files with documentation comments, for
instance — and you set
max_tokens, you may need to raise the limit. Thetoken_countattribute of a parsed document likewise now includes comments.Workarounds
If you cannot upgrade, apply a request body-size limit to the decompressed body
before the document reaches
parse().Such a limit is worth keeping after upgrading as well:
max_tokensis one guard amongseveral and bounds the token count, not the overall cost of a request.
References
Credits
Reported by @BZHunt, who also reviewed the fixes
before release. In memory of ZaX, who was the driving force behind this discovery.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
GraphQL-core: Denial of service via quadratic complexity in the OverlappingFieldsCanBeMerged validation rule
CVE-2026-75507 / GHSA-vj8h-fx38-h3vc
More information
Details
Summary
The
OverlappingFieldsCanBeMergedvalidation rule can be driven into quadratic — andin the worst case
O(n²·m²)— running time by a small, highly compressible query. Asingle 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_rulesand therefore enabled by default, every GraphQL-core based server is affected at
default settings.
Details
OverlappingFieldsCanBeMergedcompares every pair of fields that share a responsename and recurses into their sub-selections. The memoization added for
CVE-2023-26144 (
GHSA-9pv7-vfvm-6vr7) is keyed by named fragment identity, so itdoes 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
Measured on GraphQL-core 3.2.8 / CPython 3.12 / Linux x86_64:
Doubling
nat fixedmquadruples the time, as expected for a quadratic outer loopdriving a quadratic inner walk.
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.
Patches
Fixed in 3.2.12 and 3.3.0rc1.
Validation now enforces a per-document comparison budget. Once
MAX_FIELD_COMPARISONS(default250_000) pairwise comparisons have been made, therule aborts and reports a
GraphQLErrorinstead of running to completion:The default is far above what any realistic query reaches. It is a module-level
constant and can be raised or lowered:
Note the behaviour change: a pathological query that previously validated — slowly —
now produces a validation error.
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. Removing
OverlappingFieldsCanBeMergedRulefrom the rule set is not a safe workaround: it isa 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.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Release Notes
graphql-python/graphql-core (graphql-core)
v3.2.12Compare Source
Patch-release GraphQL-core v3.2.12, based on GraphQL.js v16.14.2.
This patch-release supports Python 3.7 to 3.14.
This is a security release. It fixes two denial-of-service vulnerabilities in the validator and the parser. Upgrading is recommended for all users.
Security fixes:
OverlappingFieldsCanBeMergedvalidation rule could be driven into quadratic and worse running time by a small, highly compressible query using repeated inline fragments, occupying a worker for minutes of CPU during validation, before any resolver runs. Validation now enforces a per-document comparison budget and aborts with aGraphQLErroronce it is exhausted.max_tokenslimit, so a comment-padded document could bypass the limit while allocating one token object per comment.max_tokensnow counts every token the parser consumes, including skipped comments.Please note the two behaviour changes this implies:
graphql.validation.rules.overlapping_fields_can_be_merged.MAX_FIELD_COMPARISONS.max_tokens, so a comment-heavy document that was previously accepted may now be rejected. If you parse documents with substantial comments and setmax_tokens, you may need to raise the limit. Thetoken_countof a parsed document now includes comments as well.Both limits complement, but do not replace, a request body-size limit applied to the decompressed request body in your web framework.
Other changes:
The security issues were reported by @BZHunt, who also reviewed the fixes before release. In memory of ZaX, who was the driving force behind this discovery. The first one was also independently found and diagnosed by @bcmyguest.
Thanks to @jkimbo for sponsoring this project.
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.