Jun 4, 2026·6 min read·8 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.
A stored Cross-Site Scripting (XSS) vulnerability exists within plone.restapi, the REST API package for Plone content management system. By supplying a spoofed input MIME type (text/x-html-safe), an attacker can mislead the rendering layer (plone.app.textfield) into assuming that the supplied content is already sanitized. This causes the system to skip the safe_html transform, allowing arbitrary JavaScript to execute in the victim's browser when they view the compromised page.
An untrusted search path vulnerability in the GlobalDatabasePlugin component of the AWS Advanced JDBC Wrapper for Amazon Aurora PostgreSQL allows authenticated, low-privilege database users to hijack administrative session queries. By defining a custom function in a writable schema such as the public schema, an attacker can hijack queries executed automatically during driver-level topology detection. When a highly privileged database user connects to the database utilizing an affected version of the wrapper, the custom function executes under their security context, enabling remote privilege escalation to rds_superuser.
CVE-2026-27771 represents a critical security flaw in Gitea and Forgejo (up to and including version 1.26.1) involving missing authorization checks (CWE-862). Unauthenticated remote attackers can query, enumerate, and download private container images from the OCI-compliant container registry. Additionally, unauthorized users can retrieve private or internal source repository URLs via the Composer package registry metadata API. A public proof-of-concept exists, and threat metrics indicate highly active scanning and exploitation risks.
A missing authorization vulnerability in the Formie plugin for Craft CMS prior to version 3.1.28 allows low-privileged Control Panel users to read and modify sensitive administrative settings, configuration options, and third-party integrations.
CVE-2026-53598 is a directory traversal and arbitrary file read vulnerability in Microsoft Prompty ecosystem loaders across multiple languages. Prior to version 2.0.0-beta.2, the loaders resolved `${file:...}` reference strings inside frontmatter configuration blocks without enforcing that the target file paths resided within authorized directories. This deficiency allows an attacker-controlled configuration file to read sensitive operating system and application files through absolute paths, directory traversal, or symbolic link escapes. The issue is addressed across the Python, C#, Node.js/TypeScript, and Rust ecosystems.
A directory traversal vulnerability exists in the copy subcommand of the proot-distro utility. Due to incomplete path sanitization, local attackers or malicious scripts can read from or write to arbitrary files outside the container rootfs, bypassing isolation barriers and potentially gaining unauthorized access or persistent execution on the host system.