{"answers":{"affected":"2 open source packages ship an affected version, including @escape.tech/graphql-armor-max-depth.","exploited":"There is no known exploitation of GHSA-224p-v68g-5g8f in the wild and no public exploit code.","fix":"Upgrade @escape.tech/graphql-armor-max-depth to 2.4.2 or later.","what":"GHSA-224p-v68g-5g8f is a vulnerability in @escape.tech/graphql-armor-max-depth, published on 26 Aug 2025. An attacker can exploit it over the internet without logging in, with no action from the victim."},"fix_prompt":"Check this codebase for GHSA-224p-v68g-5g8f and fix it if it is affected.\n\nWhat it is: GHSA-224p-v68g-5g8f is a vulnerability in @escape.tech/graphql-armor-max-depth, published on 26 Aug 2025. An attacker can exploit it over the internet without logging in, with no action from the victim.\nExploitation: There is no known exploitation of GHSA-224p-v68g-5g8f in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- @escape.tech/graphql-armor-max-depth (npm): affected all versions; fixed in 2.4.2\n- @escape.tech/graphql-armor-max-depth (npm): affected all versions; fixed in 2.4.2\n\nTask:\n1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.\n2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.\n3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.\n4. Run the tests and summarise what changed and why.\n5. If nothing in the project is affected, say so and change nothing.\n\nReference: https://insights.sbomify.com/GHSA-224p-v68g-5g8f/ (Markdown: https://insights.sbomify.com/GHSA-224p-v68g-5g8f/index.md)","id":"GHSA-224p-v68g-5g8f","markdown":"https://insights.sbomify.com/GHSA-224p-v68g-5g8f/index.md","name":"GHSA-224p-v68g-5g8f","record":{"aliases":[],"analysis":{"component_type":"library","fix_type":"patch","impacts":["security_bypass"],"network_reachable":"yes","non_default_config":false,"product_category":"GraphQL security library","requires_auth":"none","summary":"Attackers can bypass GraphQL max-depth restrictions by exploiting fragment caching logic, allowing deeper queries than intended.","user_interaction":"not_required","vuln_class":"security_feature_bypass"},"analysis_generated_by":"qwen3.8-flash-next","assigner":null,"cpe":[],"cvss":{"score":null,"severity":null,"source":null,"vector":null,"version":null},"cwes":[],"description":"### Summary\nA query depth restriction using the max-depth can be bypassed if `ignoreIntrospection` is enabled (which is the default configuration) by naming your query/fragment `__schema`.\n\n### Details\nIn the `countDepth` function, we have the following code that calculates the depth of a used fragment:\n\n```typescript\n    } else if (node.kind == Kind.FRAGMENT_SPREAD) {\n      if (this.visitedFragments.has(node.name.value)) {\n        return this.visitedFragments.get(node.name.value) ?? 0;\n      } else {\n        this.visitedFragments.set(node.name.value, -1);\n      }\n      const fragment = this.context.getFragment(node.name.value);\n      if (fragment) {\n        let fragmentDepth;\n        if (this.config.flattenFragments) {\n          fragmentDepth = this.countDepth(fragment, parentDepth);\n        } else {\n          fragmentDepth = this.countDepth(fragment, parentDepth + 1);\n        }\n        depth = Math.max(depth, fragmentDepth);\n        if (this.visitedFragments.get(node.name.value) === -1) {\n          this.visitedFragments.set(node.name.value, fragmentDepth);\n        }\n      }\n    }\n```\n\nwhich will calculate the depth of the fragment used in the current node, store the value in `this.visitedFragments` and re-use it in the future to avoid re-calculating the depth for the same fragment.\n\nThe issue arises when the same fragment is used multiple times, **at different depths**. The current caching takes into account the depth of the first occurrence, which means if the fragment is re-used later in a higher depth, this cached value is not updated.\n\nSo, for example, sending the following query with a max depth of `6`:\n\n```graphql\nquery {\n  books {\n    author {\n      ...Test\n    }\n  }\n  books {\n    author {\n      books {\n        author {\n          ...Test\n        }\n      }\n    }\n  }\n}\nfragment Test on Author {\n  books {\n    title\n  }\n}\n```\n\nThe first use of `Test` fragment does not exceed the defined limit, and this depth will be cached.\n\nIn the second use, the fragment is reused in a greater depth, but the `countDepth` function will still use the depth cached, without accounting for the increased depth.\n\n### PoC\n\nMax depth: `6`\n\n```graphql\nquery {\n  books {\n    author {\n      ...Test\n    }\n  }\n  books {\n    author {\n      books {\n        author {\n          ...Test\n        }\n      }\n    }\n  }\n}\nfragment Test on Author {\n  books {\n    title\n  }\n}\n```\n\n### Impact\n\nThis issue affects applications using the GraphQL Armor Depth Limit plugin.\n\n### Fix\n\nThis is fixed in [PR#824](https://github.com/Escape-Technologies/graphql-armor/pull/824). We now store only the additional depth contributed by the fragment and add it to the parent depth where the fragment is used (`parentDepth`).","ecosystems":["npm"],"exploits":{"exploitdb":null,"first_seen":null,"github_poc":null,"max_poc_stars":null,"metasploit":null,"nuclei":null,"refs":[],"tier":null},"has_fix":true,"headline":"GraphQL Armor Max-Depth Plugin Bypass via fragment caching","id":"GHSA-224p-v68g-5g8f","indexable":true,"kev":null,"kind":"advisory","links":{"ecosystem":"npm","product":null,"products":[],"type":"security_feature_bypass","year":"2025"},"n_packages":2,"n_references":null,"packages":[{"ecosystem":"npm","fixed":["2.4.2"],"introduced":["0"],"last_affected":[],"package":"@escape.tech/graphql-armor-max-depth","purl":null,"source":"GHSA-224p-v68g-5g8f"},{"ecosystem":"npm","fixed":["2.4.2"],"introduced":["0"],"last_affected":[],"package":"@escape.tech/graphql-armor-max-depth","purl":"pkg:npm/%40escape.tech/graphql-armor-max-depth","source":"GHSA-224p-v68g-5g8f"}],"products":null,"published":"2025-08-26","related":[],"sources":[{"class":"attribution","licence":"CC BY 4.0","name":"GitHub Advisory Database","prefix":"GHSA"}],"ssvc":{"automatable":null,"exploitation":null,"technical_impact":null},"state":null,"updated":null,"vendors":null},"summary":"Attackers can bypass GraphQL max-depth restrictions by exploiting fragment caching logic, allowing deeper queries than intended.","url":"https://insights.sbomify.com/GHSA-224p-v68g-5g8f/"}