Jun 4, 2026·6 min read·4 visits
A recursive fragment loop triggers a RecursionError in Python, crashing worker threads/processes and resulting in complete Denial of Service.
An application-level Denial of Service vulnerability exists in the Strawberry GraphQL library (versions 0.71.0 through 0.315.6) due to uncontrolled recursion within the QueryDepthLimiter and MaxAliasesLimiter extensions when processing circular fragment references.
Strawberry GraphQL is an open-source Python library designed to build GraphQL APIs using type hints. The library provides optional security extensions to safeguard endpoints against resource exhaustion attacks. Two of these extensions, QueryDepthLimiter and MaxAliasesLimiter, are designed to restrict the complexity of incoming queries before execution.\n\nThese safety components are exposed to the public attack surface because they parse and validate user-supplied GraphQL queries. When an unauthenticated remote user submits a query, the validation engine processes the payload to enforce configured limits on query depth and aliases. If a query contains nested or cyclic structures, the validation logic traverses the entire document structure.\n\nA design flaw in versions prior to 0.315.7 allows an attacker to exploit the validation logic itself. By sending queries with circular fragment definitions, an attacker can bypass the safety controls and trigger an unhandled exception. This results in an application-level Denial of Service (DoS) by crashing the active Python worker thread or process.
The core issue is classified as CWE-674: Uncontrolled Recursion, which leads to CWE-400: Uncontrolled Resource Consumption. Within the QueryDepthLimiter extension, the determine_depth function traverses the selection sets of a GraphQL query. When the traversal encounters a FragmentSpreadNode, it retrieves the corresponding fragment definition and recursively invokes determine_depth to evaluate its inner depth.\n\nIn vulnerable versions of the library, the recursion occurred without tracking state or maintaining a history of previously visited nodes in the active path. The implementation did not pass a collection of traversed fragment identifiers down the call stack. Because of this omission, the algorithm cannot detect when it re-evaluates a fragment that is already present in its current call hierarchy.\n\nWhen a client submits a query where Fragment A references Fragment B, and Fragment B references Fragment A, a mutual recursion loop is established. The determine_depth function calls itself infinitely, executing until it consumes all available stack frames. Python runtime environments strictly limit the maximum recursion depth, throwing a RecursionError and terminating execution when the limit is breached.
The fix implemented in commit a69221fb0b86583ceb5755758b294c8319021fd1 introduces path-based cycle detection using Python's immutable frozenset type. This prevents infinite loop execution by tracking the names of resolved fragments across the active execution branch. The determine_depth function was updated to accept a visited_fragments parameter.\n\npython\n# Patched version of determine_depth\ndef determine_depth(\n # ... standard parameters ...\n visited_fragments: frozenset[str] | None = None,\n) -> int:\n if visited_fragments is None:\n visited_fragments = frozenset()\n\n\nDuring traversal, when a FragmentSpreadNode is encountered, the engine checks if the fragment name already exists in the visited_fragments set. If a match is found, the function returns a depth of 0 immediately, breaking the loop safely. If the fragment is unvisited, the traversal continues, appending the current fragment name to a new frozenset using the union operator.\n\npython\n if isinstance(node, FragmentSpreadNode):\n fragment_name = node.name.value\n if fragment_name in visited_fragments:\n return 0 # Break circular recursion\n\n return determine_depth(\n node=fragments[fragment_name],\n # ... other parameters ...\n visited_fragments=visited_fragments | {fragment_name},\n )\n\n\nThis pattern is highly effective because frozenset is immutable. Using the union operator (visited_fragments | {fragment_name}) passes a distinct, branch-specific copy down the recursion tree. This avoids corrupting or sharing visited state with parallel, non-cyclic sibling query branches, ensuring both correctness and safety.\n\nmermaid\ngraph LR\n A["determine_depth(A)"] --> B["determine_depth(B)"]\n B --> C["determine_depth(A) - Triggers Break"]\n
An attacker can exploit this vulnerability with a single crafted HTTP POST request containing a circular fragment reference. Since the parsing and validation phases execute before any authentication middleware or resolvers, this attack does not require valid credentials. The target endpoint must have the QueryDepthLimiter or MaxAliasesLimiter extension enabled to be vulnerable.\n\nThe exploit payload defines two fragments that mutually spread each other. For example, Fragment A references B, and Fragment B references A. A top-level query then references Fragment A. When the GraphQL validation engine processes the query, it invokes the vulnerable depth limiter logic, initiating the recursive loop.\n\ngraphql\nfragment A on User {\n ...B\n}\nfragment B on User {\n ...A\n}\nquery Exploit {\n me {\n ...A\n } \n}\n\n\nOnce submitted, the worker thread begins processing determine_depth for Fragment A, which calls Fragment B, which calls Fragment A. Within milliseconds, the stack depth exceeds Python's threshold. The resulting RecursionError escapes the validation context, crashing the underlying process and denying service to other concurrent requests.
The primary impact of CVE-2026-47706 is a complete Denial of Service (DoS) of the application layer. When Python processes throw a RecursionError during query validation, the exception is frequently unhandled by the ASGI or WSGI application servers. This triggers a hard crash of the active worker process.\n\nIn standard deployments, application servers such as Gunicorn or Uvicorn maintain a fixed pool of worker processes to handle incoming requests. If an attacker sends concurrent requests containing the circular exploit payload, they can systematically crash every available worker process in the pool. This leads to a total service outage for all users.\n\nAlthough the vulnerability does not lead to remote code execution (RCE) or data confidentiality breaches, its ease of exploitation makes it a significant operational risk. Because the validation phase occurs prior to authentication, any unauthenticated user with network access to the GraphQL endpoint can trigger the crash. The CVSS score of 5.3 reflects this focused impact on service availability.
The primary mitigation is upgrading the strawberry-graphql package to version 0.315.7 or later. This version incorporates cycle-tracking logic into both QueryDepthLimiter and MaxAliasesLimiter to safely handle cyclic structures. Organizations should verify their dependency locks and rebuild containers to ensure the patch is applied.\n\nIf upgrading is not immediately feasible, teams can disable the QueryDepthLimiter and MaxAliasesLimiter extensions as a temporary workaround. Note that disabling these extensions will expose the application to other query complexity attacks. Therefore, this action should only be taken if alternative mitigations are implemented.\n\nAn alternative workaround involves deploying a GraphQL-aware Web Application Firewall (WAF) or an API gateway (such as Apollo Router or Envoy) at the network perimeter. The gateway can be configured to validate fragment relationships and reject queries containing circular definitions before they reach the upstream Python application servers.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
strawberry-graphql Strawberry GraphQL | >= 0.71.0, <= 0.315.6 | 0.315.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-674 / CWE-400 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 5.3 (Medium) |
| Exploit Status | Proof of Concept Available |
| CISA KEV Status | Not Listed |
| Impact | Availability (Denial of Service) |
The software directs a function to call itself recursively without a mechanism to limit the recursion depth, leading to stack exhaustion.
React Router and the underlying turbo-stream vendor library contain a vulnerability allowing remote unauthenticated attackers to trigger a Denial of Service (DoS) or potentially client-side Cross-Site Scripting (XSS) due to unsafe dynamic deserialization of streaming error payloads.
A security flaw in strawberry-graphql versions 0.172.0 through 0.315.6 allows unauthenticated attackers to bypass the MaxAliasesLimiter extension. By utilizing GraphQL fragment spreads, clients can trigger high levels of alias amplification, causing uncontrolled backend resource consumption and application-level Denial of Service.
CVE-2026-48710 is a critical security-desynchronization vulnerability in the Starlette ASGI framework (versions >= 0.8.3, < 1.0.1) that allows remote attackers to bypass path-based security middleware and access-control decorators. By injecting URI authority-to-path delimiters into the Host header, attackers can manipulate the application-level parsed URL path while the underlying ASGI server dispatches the request to target endpoints.
CVE-2026-20230 is a critical Server-Side Request Forgery (SSRF) vulnerability in the WebDialer service of Cisco Unified Communications Manager (Unified CM) and Cisco Unified Communications Manager Session Management Edition (Unified CM SME). The flaw arises from improper validation of input parameters within WebDialer HTTP requests. Unauthenticated remote attackers can exploit this vulnerability to force the application to make HTTP requests to internal administrative services bound to the loopback interface. In the Cisco Voice Operating System (VOS) environment, these local services trust loopback traffic inherently, permitting unauthorized file writes. By writing malicious files to specific system directories, the attacker can execute arbitrary commands with root privileges.
CVE-2026-48526 is an algorithm-confusion vulnerability in PyJWT prior to version 2.13.0. When an application decodes tokens using a raw JSON Web Key (JWK) string while simultaneously supporting mixed algorithm families (symmetric and asymmetric), PyJWT does not validate that the key matches its intended algorithm context. This allows an attacker to sign a forged token using the public JWK string as an HMAC symmetric secret, bypassing authentication controls.
CVE-2026-23479 is a critical Use-After-Free (UAF) vulnerability inside the blocking-client code path of the Redis in-memory data structure server. In affected versions from 7.2.0 until 8.6.3, the unblock client flow fails to handle an error return from processCommandAndResetClient when re-executing a previously blocked command. If a blocked client is evicted due to maxmemory limits or client eviction policies during this command processing flow, its client structure is freed. Because the caller ignores the error return and continues processing, it attempts to read and write properties on the freed client structure, leading to a Use-After-Free condition.