{"id":"GHSA-h3hw-29fv-2x75","aliases":[],"url":"https://o3.security/vulnerability/GHSA-h3hw-29fv-2x75","summary":"@envelop/graphql-modules has a Race Condition vulnerability","details":"### Summary\nContext race condition when using `useGraphQLModules` plugin\n\n### Details\n\nRelated to: https://github.com/graphql-hive/graphql-modules/security/advisories/GHSA-53wg-r69p-v3r7\n\nWhen 2 or more parallel requests are made which trigger the same service, the context of the requests is mixed up in the service when the context is injected via @ExecutionContext() and graphql-modules are used in Yoga with `useGraphQLModules(application)`. This issue was fixed in `graphql-modules` in `2.4.1` and `3.1.1` but using `useGraphQLModules` will bypass the `async_hooks` fix that was implemented.\n\n### PoC\n\nCreate the following `package.json` and run `npm i`\n\n```json\n{\n  \"name\": \"poc\",\n  \"scripts\": {\n    \"compile\": \"tsc\",\n    \"start\": \"npm run compile && node ./dist/src/index.js\",\n    \"test\": \"npm run compile && node ./dist/test/bleedtest.js\"\n  },\n  \"dependencies\": {\n    \"@envelop/graphql-modules\": \"^9.0.0\",\n    \"graphql-yoga\": \"^5.0.0\",\n    \"graphql\": \"^16.10.0\",\n    \"graphql-modules\": \"3.1.1\",\n    \"reflect-metadata\": \"0.2.1\",\n    \"axios\": \"^1.8.4\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^22.14.1\",\n    \"typescript\": \"^5.8.3\"\n  }\n}\n```\n\nDefine the app entrypoint: `src/index.ts`\n\n```ts\nimport { module } from \"./module.js\";\nimport { useGraphQLModules } from '@envelop/graphql-modules'\nimport { createApplication } from \"graphql-modules\";\nimport { createServer } from 'node:http'\nimport { randomUUID } from \"node:crypto\";\nimport { createYoga } from 'graphql-yoga';\n\nconst application = createApplication({\n    modules: [module]\n})\n\nconst yoga = createYoga({\n    schema: application.schema,\n    plugins: [useGraphQLModules(application)],\n    context() {\n        return {\n            requestId: randomUUID(),\n        }\n    }\n})\n\nconst server = createServer(yoga)\nserver.listen(4001, '127.0.0.1', undefined, () => {\n    console.info(\n        `[Server] Running on http://localhost:4001/graphql`\n    )\n})\n```\n\nCreate the test module: `src/module.ts`\n\n```ts\nimport { createModule, gql } from \"graphql-modules\";\nimport Service from \"./service.js\";\n\nconst typeDefs = gql`\n    type Book {\n        id1: String\n        id2: String\n    }\n    type Query {\n        books: [Book]\n    }\n`;\n\nexport const module = createModule({\n    id: 'book-module',\n    typeDefs: [typeDefs],\n    providers: [Service],\n    resolvers: {\n        Query: {\n            // return one empty book\n            books: () => [{}],\n        },\n        Book: {\n            // return the requestId from the context\n            id1: async (_root, _args, { injector } ) => {\n                return injector.get(Service).get();\n            },\n            // return the requestId from the context of the service 100 ms later\n            id2: async (_root, _args, { injector } ) => {\n                await new Promise(resolve => setTimeout(resolve, 100));\n                return injector.get(Service).get()\n            },\n        }\n    }\n})\n```\n\nAdd the Service that's to be injected `src/service.ts`\n\n```ts\nimport { ExecutionContext, Injectable } from 'graphql-modules';\nimport 'reflect-metadata';\n\n@Injectable()\nexport default class Service {\n    @ExecutionContext()\n    private context: ExecutionContext;\n\n    get() {\n        return this.context.requestId;\n    }\n}\n```\n\nAdd the test case `test/bleedtest.js`\n\n```ts\nimport axios from 'axios';\n\nconst url = 'http://localhost:4001/graphql';\nconst query = `query { books { id1 id2 } }`;\n\nconst makeGraphQLRequest = async () => {\n    const response = await axios.post(url, { query });\n\n    const book = response.data.data.books[0]\n    if (book.id1 !== book.id2) {\n        throw new Error(`wrong response with ids ${(book.id1)} and ${(book.id2)}`)\n    }\n}\n\nconst numberOfRequests = 2;\nawait Promise.all(Array.from(\n    { length: numberOfRequests },\n    makeGraphQLRequest,\n));\n```\n\nThen run the server with `npm run start` in one terminal and the testcase in another with `npm run test`.\nThe returned IDs should be identical as they are both read from the context within the same request.\nHowever, there is a mismatch:\n\n```\n❯ npm run test\n\n> poc@1.0.0 test\n> npm run compile && node ./dist/test/bleedtest.js\n\n\n> poc@1.0.0 compile\n> tsc\n\nfile://<redacted>/dist/test/bleedtest.js:8\n        throw new Error(`wrong response with ids ${(book.id1)} and ${(book.id2)}`);\n              ^\n\nError: wrong response with ids c2d83151-0922-4f25-a3e9-2f03acc1376a and e16c7335-0eaa-4386-b415-869ee4b05315\n    at makeGraphQLRequest (file://<redacted>/dist/test/bleedtest.js:8:15)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async Promise.all (index 0)\n    at async file://<redacted>/dist/test/bleedtest.js:12:1\n```\n\n### Impact\nAny application that uses `useGraphQLModules` from `@envelop/graphql-modules` along with services that inject the context using @ExecutionContext() from a singleton provider are at risk. The more traffic an application has, the higher the chance for parallel requests, the higher the risk.","published":"2026-01-21T16:36:27Z","modified":"2026-02-03T03:04:17.118258Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"@envelop/graphql-modules","fixedVersion":"9.1.0"}],"fix":{"url":"https://github.com/graphql-hive/envelop/commit/ab49fa259a51d976c437e084114e070b99720ba9","label":"graphql-hive/envelop@ab49fa2"},"references":[{"type":"WEB","url":"https://github.com/graphql-hive/envelop/security/advisories/GHSA-h3hw-29fv-2x75"},{"type":"WEB","url":"https://github.com/graphql-hive/graphql-modules/security/advisories/GHSA-53wg-r69p-v3r7"},{"type":"WEB","url":"https://github.com/graphql-hive/envelop/commit/ab49fa259a51d976c437e084114e070b99720ba9"},{"type":"PACKAGE","url":"https://github.com/graphql-hive/envelop"},{"type":"WEB","url":"https://github.com/graphql-hive/envelop/releases/tag/release-1768928934700"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-02-03T03:04:17.118258Z"}}