{"id":"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, () =\u003e {\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: () =\u003e [{}],\n        },\n        Book: {\n            // return the requestId from the context\n            id1: async (_root, _args, { injector } ) =\u003e {\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 } ) =\u003e {\n                await new Promise(resolve =\u003e 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 () =\u003e {\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\u003e poc@1.0.0 test\n\u003e npm run compile && node ./dist/test/bleedtest.js\n\n\n\u003e poc@1.0.0 compile\n\u003e tsc\n\nfile://\u003credacted\u003e/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://\u003credacted\u003e/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://\u003credacted\u003e/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.","modified":"2026-02-03T03:04:17.118258Z","published":"2026-01-21T16:36:27Z","database_specific":{"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2026-01-21T16:36:27Z","nvd_published_at":null,"cwe_ids":["CWE-362"]},"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"}],"affected":[{"package":{"name":"@envelop/graphql-modules","ecosystem":"npm","purl":"pkg:npm/%40envelop/graphql-modules"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"9.1.0"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/01/GHSA-h3hw-29fv-2x75/GHSA-h3hw-29fv-2x75.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N"}]}