{"id":"CVE-2026-118666","kind":"vulnerability","aliases":["CVE-2026-118666","GHSA-mp66-8ww2-pcwm"],"related":[],"title":"GraphQL Tools: Shared legacy executor routes one subscription's results to a different caller","description":"# Cross-tenant subscription data leak in `@graphql-tools/executor-legacy-ws`: a shared `websocket.onmessage` is replaced on every `subscribe()`, and `DATA` frames are delivered without checking the protocol operation id\n\n## Report metadata\n\n| Field | Value |\n| --- | --- |\n| Package | `@graphql-tools/executor-legacy-ws` (npm, `pkg:npm/%40graphql-tools/executor-legacy-ws`) |\n| Repository | https://github.com/ardatan/graphql-tools |\n| Component | `buildWSLegacyExecutor` → the returned executor's `subscribe(observer)` / `websocket.onmessage` handler |\n| Vulnerability class | Cross-tenant data leak (subscription results delivered to the wrong caller) |\n| Verified against | Source at `b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1` (package version 1.1.33); published npm tarball `1.1.33` with matching source at `d304214db06e8a4ce9cb41138f199e3bc6ed36f2`; the same behaviour also reproduces on the current release `1.1.35` |\n| API surface | Public documented API (`buildWSLegacyExecutor`) |\n| Affected in default configuration | Yes — `ensureWebsocket()` reuses an already-`OPEN` socket (`index.ts:35-42`), and the socket has a single mutable `onmessage` property (`index.ts:113`). No single-operation-only restriction is documented, and the return type is the general reusable `DisposableExecutor` (`packages/utils/src/executor.ts:34-57`) |\n\nAffected version range: the range recorded for this finding is `<= 1.1.33`, but that is understated — the same proof of concept reproduces on `1.1.35`, and a byte diff of the built CJS output between `1.1.33` and `1.1.35` shows only one unrelated change (`rejectUnauthorized`).\n\n## Summary\n\n`buildWSLegacyExecutor` keeps one WebSocket in closure state and installs the message handler by assigning to the socket's single `websocket.onmessage` property inside `subscribe()`. Every new subscription therefore overwrites the previous subscriber's handler, and the handler's `case LEGACY_WS.DATA` branch calls `observer.next(data.payload)` without ever comparing `data.id` to the operation id that this subscription created at line 103 and sent with `START` at line 123. When one executor instance is reused for overlapping subscriptions, the most recent caller receives payloads belonging to the earlier caller, and the earlier caller receives nothing.\n\nIn a consuming application, the tainted input is an ordinary, correctly-tagged `DATA` frame emitted by the legitimate downstream server for the victim's operation. The attacker does not need to control or compromise that server; they only need to be able to start their own subscription through the same shared executor while the victim's subscription is active — the natural shape of a gateway, stitched schema, or any service that builds a remote executor once and reuses it per request.\n\nThe surrounding code shows the inconsistency:\n\n- `packages/loaders/url/src/index.ts:262-295` (in-repo `UrlLoader` wrapper) → constructs a fresh legacy executor inside each request, so that caller avoids the shared state.\n- `buildWSLegacyExecutor`'s returned executor, reused across callers → **unsafe: shared `onmessage` overwritten at line 113, `DATA` dispatched without an id check at lines 145-147**.\n- `packages/loaders/url/src/index.ts:172-182` → also a production caller of the legacy executor; safe only insofar as the wrapper above rebuilds the executor per request, not because of any check in the executor itself.\n\n## Impact\n\nA user of a service that shares one legacy executor receives another user's private GraphQL subscription payloads verbatim, with the victim's authorization context, without ever having been authorized for that operation. The victim is simultaneously starved: their own iterator stops receiving the events they subscribed to, because their handler has been detached.\n\nWithout this bug, the attacker's observer would receive only results for their own operation — and in this implementation, no result at all, since their operation is never `START`ed after `CONNECTION_ACK` has already fired. The new capability is therefore reading data the attacker could not otherwise obtain by any request they are permitted to make, plus a denial of the victim's subscription stream.\n\nScoring note: no CVSS vector was computed for this finding, because exploitability depends on the consuming application's data flow — specifically whether one executor instance is shared across mutually untrusted callers. Consumers that construct a new executor per operation (as the in-repo `UrlLoader` wrapper does at `packages/loaders/url/src/index.ts:262-295`) are not affected by the cross-caller leak. The set of affected downstream consumers has not been enumerated: **unknown**.\n\n## Preconditions\n\n1. One executor value returned by `buildWSLegacyExecutor` is reused rather than constructing a new executor per operation. This is the normal use of an `Executor`: the contract is invoked once per `ExecutionRequest`, the code explicitly reuses an `OPEN` socket, and the returned value holds `Symbol.dispose`.\n2. Two subscription iterators overlap on an already-`OPEN` socket. The deciding default is in `ensureWebsocket()` (`packages/executors/legacy-ws/src/index.ts:35-42`), which does not create a second socket when one is open, combined with the single mutable `websocket.onmessage` assignment at line 113.\n3. The attacker can cause their own subscription request to be made through that shared executor while the victim's subscription is active and acknowledged. They do not need any control over the downstream server.\n4. Downstream subscription results differ by user or authorization context, so a misrouted payload is actually a disclosure.\n\nThis does **not** require non-standard usage, monkey-patching, or unusual configuration. `packages/executors/legacy-ws/src/index.ts` documents no single-operation-only restriction, the return type is the general reusable `DisposableExecutor` (`packages/utils/src/executor.ts:34-57`), and the behaviour is not documented as a caller responsibility.\n\n## Data flow\n\n1. **Source** — a legitimate `DATA` message emitted by the downstream server for *another* operation on the shared WebSocket. The victim invokes the returned executor, and `subscribe()` installs a closure over the victim's observer and request as `websocket.onmessage` ([`packages/executors/legacy-ws/src/index.ts:90-113`](https://github.com/ardatan/graphql-tools/blob/b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1/packages/executors/legacy-ws/src/index.ts#L90-L113)).\n2. **Propagation** — the server's `CONNECTION_ACK` starts the victim's operation with `id`, created at line 103 and sent with `START` at line 123 ([`packages/executors/legacy-ws/src/index.ts:116-135`](https://github.com/ardatan/graphql-tools/blob/b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1/packages/executors/legacy-ws/src/index.ts#L116-L135)).\n3. **Sink** — the attacker invokes the same executor while `websocket.readyState` is `OPEN`; `ensureWebsocket()` does not create a new socket and `subscribe()` replaces the sole `websocket.onmessage` with the attacker's closure ([`index.ts:35-42`](https://github.com/ardatan/graphql-tools/blob/b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1/packages/executors/legacy-ws/src/index.ts#L35-L42), [`index.ts:109-113`](https://github.com/ardatan/graphql-tools/blob/b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1/packages/executors/legacy-ws/src/index.ts#L109-L113)). The server's `DATA` frame for the victim carries the victim's operation id per the legacy protocol, but the handler ignores `data.id` and calls `observer.next(data.payload)` on the attacker's observer ([`index.ts:114-147`](https://github.com/ardatan/graphql-tools/blob/b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1/packages/executors/legacy-ws/src/index.ts#L114-L147)).\n4. **Reachability** — the sink sits in the only `subscribe` path of the executor produced by the directly exported `buildWSLegacyExecutor` ([`index.ts:27-31`](https://github.com/ardatan/graphql-tools/blob/b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1/packages/executors/legacy-ws/src/index.ts#L27-L31)). `packages/loaders/url/src/index.ts:172-182` is a production caller in this repository; direct library consumers reach it by building a remote executor once and reusing it.\n\n## Vulnerable code\n\n`packages/executors/legacy-ws/src/index.ts`, lines 103-167 at `b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1`:\n\n```ts\nconst id = Date.now().toString();\nreturn observableToAsyncIterable({\n  subscribe(observer) {\n    ensureWebsocket();\n    ...\n    websocket.onmessage = event => {           // sole handler: overwritten by every new subscribe()\n      const data = JSON.parse(event.data.toString('utf-8'));\n      switch (data.type) {\n        ...\n        case LEGACY_WS.DATA: {\n          observer.next(data.payload);         // SINK: data.id is never compared with id\n          break;\n        }\n      }\n    };\n  },\n});\n```\n\nTwo independent defects are visible here: the single mutable `onmessage` assignment (which detaches the earlier subscriber), and the missing `data.id === id` check in the `DATA` branch (which misroutes the payload). The operation id itself is also derived from `Date.now()`, so it is not collision-resistant.\n\n## Reproduction\n\nVerified dynamically against the real published package `@graphql-tools/executor-legacy-ws@1.1.33` (npm tarball with matching source at `d304214db06e8a4ce9cb41138f199e3bc6ed36f2`). The harness was a protocol-correct legacy `graphql-ws` server (ws v8) that tags every `DATA` frame with the operation id of the operation that produced it, plus two callers sharing one executor instance.\n\n- **Scenario A (control, single subscription):** the subscriber received its own payload, confirming the harness is sound.\n- **Scenario B (the claim):** caller 1 (\"victim\") subscribed; the server ACKed and the client sent `start id=1790247741390`. Caller 2 (\"attacker\") then invoked the **same** executor while `readyState` was `OPEN`. The server observed only one socket (`ensureWebsocket` reused it) and only one started operation — the attacker's operation was never started, because `CONNECTION_ACK` had already fired. The server then emitted a correctly-tagged `DATA` frame for the victim's id. The attacker's iterator received `{\"data\":{\"victimSecret\":\"VICTIM-PRIVATE-DATA-a1b2c3\"}}` and the victim's iterator received nothing.\n- **Scenario C:** a `DATA` frame tagged with the id `\"id-that-was-never-started\"` was still delivered to the subscriber, proving the protocol operation id is never validated.\n- **Realistic wiring:** with the executor wired into `wrapSchema()` the way a stitching gateway does, two concurrent users subscribing to the same field produced a verbatim cross-tenant leak — User B received `{\"data\":{\"accountBalance\":\"USER-A-PRIVATE-BALANCE-$91402.11\"}}` while User A received nothing. When the two users select different fields, the wrap layer's result transform masks the foreign field (B saw `weather:null`), but the misrouting still occurs and the victim is still starved; at the raw executor API the payload leaks verbatim regardless.\n- **Causality control:** patching only the `DATA` branch to check `data.id === id` eliminated the leak (the attacker received nothing, and the foreign-id frame was rejected), pinning the leak to the missing id check. Under that patch the victim still received nothing, confirming the second, independent defect: the single shared `websocket.onmessage` is overwritten on each `subscribe()`, detaching the earlier subscriber's handler entirely.\n- **Scope caveat:** the identical proof of concept also reproduces on the current latest release `1.1.35`.\n\nMinimal client-side shape:\n\n```ts\nimport WebSocket from 'isomorphic-ws';\nimport { parse } from 'graphql';\nimport { buildWSLegacyExecutor } from '@graphql-tools/executor-legacy-ws';\n\nconst exec = buildWSLegacyExecutor('ws://localhost:8081/graphql', WebSocket);\n\nconst victim = exec({ document: parse('subscription { privateEvent { secret } }') });\nconst victimPending = victim.next(); // causes connection + START; wait until the server has ACKed\nawait new Promise(r => setTimeout(r, 100));\n\nconst attacker = exec({ document: parse('subscription { publicEvent { id } }') });\nconst attackerPending = attacker.next(); // overwrites socket.onmessage\n\n// When the server now emits DATA for the victim's id, this prints the victim's secret.\nconsole.log(await attackerPending);\n```\n\nSteps:\n\n1. Start a normal legacy `subscriptions-transport-ws` server with a private event subscription.\n2. Build one executor and call `victimIterator.next()` so the socket receives `connection_ack` and `START` for the victim.\n3. While that socket is `OPEN`, call `attackerIterator.next()` using the same executor.\n4. Emit one victim event from the server.\n\n- **Expected:** a `DATA` frame is delivered only to the observer registered for `data.id`; the attacker's pending `next()` must never resolve with the victim's payload, and the victim keeps receiving their own events.\n- **Actual:** the attacker's `next()` resolves with the victim's payload, and the victim's iterator receives nothing.\n\n## Suggested remediation\n\nDispatch protocol frames by operation id to the observer that started that operation, instead of routing every frame to whichever subscriber most recently overwrote the socket's sole `onmessage` handler.\n\n```ts\n// 1. Stop the misrouting: ignore frames that belong to another operation.\ncase LEGACY_WS.DATA: {\n  if (data.id !== id) {\n    break;\n  }\n  observer.next(data.payload);\n  break;\n}\n```\n\n```ts\n// 2. Stop the handler overwrite: keep a registry of observers keyed by operation id\n//    and install exactly one shared message listener for the socket.\nconst observers = new Map<string, Observer<any>>();\n\nfunction ensureMessageHandler() {\n  if (messageHandlerInstalled) return;\n  messageHandlerInstalled = true;\n  websocket.onmessage = event => {\n    const data = JSON.parse(event.data.toString('utf-8'));\n    const target = data.id != null ? observers.get(data.id) : undefined;\n    // ... dispatch DATA / ERROR / COMPLETE to `target` only, and remove it on COMPLETE\n  };\n}\n```\n\nAn alternative that keeps the current structure is to create a separate socket (and therefore a separate listener) per execution, as the in-repo `UrlLoader` wrapper effectively does today.\n\nNotes:\n\n- The same id check is needed on the `ERROR` and `COMPLETE` branches, not just `DATA`; otherwise one operation's termination still tears down another's iterator.\n- The operation id is generated as `Date.now().toString()` (line 103), which is not collision-resistant — two subscriptions created in the same millisecond would share an id even after the dispatch fix. Use a counter or a random component.\n- The missing id check and the overwritten `onmessage` are two independent defects; fixing only the id check stops the leak but leaves the earlier subscriber permanently starved, as the causality control above showed.\n- `packages/loaders/url/src/index.ts:262-295` already builds a fresh legacy executor inside each request, so that caller needs no change; the fix belongs in the executor itself, because the exported `buildWSLegacyExecutor` returns a general reusable `DisposableExecutor`.\n- No upstream advisory (CVE/GHSA), fix commit, or fixed version is known for this issue: **unknown / not verified**. The set of affected downstream consumers has likewise not been enumerated.\n\nSuggested regression test alongside the existing `packages/executors/legacy-ws` test suite: with one executor shared by two overlapping subscriptions on an `OPEN` socket, assert that a `DATA` frame tagged with subscription A's operation id resolves only A's iterator and never B's, and that a `DATA` frame tagged with an id that was never started is dropped rather than delivered to the most recent subscriber.\n\n## Resolution\n\nFixed in `@graphql-tools/executor-legacy-ws@1.1.36`, which is published on npm. Each execution has its own socket, and `data`, `error`, and `complete` frames are delivered only when the protocol operation id matches. Upgrade to `>=1.1.36`.","summary":"A reused executor from @graphql-tools/executor-legacy-ws shares one WebSocket and overwrites its message handler on every subscribe, and it delivers DATA frames without checking the operation id. A second caller using the same executor while another subscription is open receives the first caller's payloads, and the first caller's stream stops.","severity":"medium","cvss":6.0,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@graphql-tools/executor-legacy-ws","fixed":["1.1.36"],"affected":"<= 1.1.35","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-200","gateways","graphql-subscriptions","graphql-tools","node","npm","package","stitched-schemas"],"not_affected_if":["new-executor-per-operation","no-legacy-ws-subscriptions"],"how_to_check":"- Run `npm ls @graphql-tools/executor-legacy-ws` to find the installed version.\n- Search your code for `buildWSLegacyExecutor` and check whether one returned executor is reused across requests or users.\n- Check whether that executor is used with `wrapSchema` or a stitched gateway for subscriptions.","fix":"- Upgrade `@graphql-tools/executor-legacy-ws` to `1.1.36` (the advisory's fixed version).\n- Until upgraded, build a new executor per operation or per user instead of sharing one instance.","status":"open","published_at":"2026-10-28T21:05:07+00:00","first_seen_at":"2026-10-28T21:07:44+00:00","triaged_at":"2026-10-28T21:09:35+00:00","updated_at":"2026-10-28T21:35:45+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-28T21:09:35Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `@graphql-tools/executor-legacy-ws` to `1.1.36` (the advisory's fixed version).\n- Until upgraded, build a new executor per operation or per user instead of sharing one instance.","reach":"internet","title":"GraphQL Tools legacy WebSocket executor leaks subscription results across callers","impact":"high","summary":"A reused executor from @graphql-tools/executor-legacy-ws shares one WebSocket and overwrites its message handler on every subscribe, and it delivers DATA frames without checking the operation id. A second caller using the same executor while another subscription is open receives the first caller's payloads, and the first caller's stream stops.","category":"package","nicknames":[],"who_cares":["npm","graphql-tools","node","graphql-subscriptions","gateways","stitched-schemas"],"confidence":0.8,"needs_auth":"unknown","worst_case":"another user's private subscription payloads are delivered to an attacker's observer","analyst_note":"","how_to_check":"- Run `npm ls @graphql-tools/executor-legacy-ws` to find the installed version.\n- Search your code for `buildWSLegacyExecutor` and check whether one returned executor is reused across requests or users.\n- Check whether that executor is used with `wrapSchema` or a stitched gateway for subscriptions.","needs_analyst":false,"not_affected_if":["new-executor-per-operation","no-legacy-ws-subscriptions"],"who_is_affected":"Applications that build one legacy WebSocket executor with `buildWSLegacyExecutor` and reuse it for overlapping subscriptions from different callers, such as gateways and stitched schemas. Consumers that create a new executor for each operation are not affected by the cross-caller leak. Whether other consumers are affected is unknown.","user_interaction":"no","needs_special_setup":"no","severity_if_unknown":"unknown"},"cost_usd":0.001108,"input_tokens":7778,"output_tokens":661,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-118666':17A,25A '-147':517C '-182':520C '-200':31B '-2026':16A,24A '-295':473C '-42':218C '-57':248C '/ardatan/graphql-tools':148C '/executor-legacy-ws':29A,52B,109C,137C,144C '1.1.33':180C,184C,259C,282C '1.1.35':199C,271C,284C '103':349C '113':507C '123':356C '145':516C '172':519C '262':472C '34':247C '40graphql':142C '40graphql-tools':141C '8ww2':21A 'abl':425C 'ack':637C 'across':499C 'activ':442C 'affect':206C,249C 'alr':639C 'alreadi':214C 'also':193C,521C 'anoth':82B,562C 'api':200C,204C 'applic':386C 'assign':305C 'attack':410C,609C 'author':574C,580C 'avoid':490C 'b5d1c36ebb8ed3868260ffee5bbcbb423ad5c1f1':177C 'behaviour':192C 'belong':372C 'branch':331C 'bug':607C 'build':455C 'buildwslegacyexecutor':150C,205C,292C,494C 'built':278C 'byte':274C 'call':332C 'caller':14A,76B,89B,95B,172C,369C,376C,380C,489C,500C,524C 'case':329C 'chang':289C 'check':70B,124C,513C,545C 'cjs':279C 'class':160C 'closur':297C 'code':467C 'compar':337C 'compon':149C 'compromis':417C 'concept':268C 'configur':209C 'connect':636C 'construct':479C 'consum':385C 'context':575C 'control':415C 'correct':394C 'correctly-tag':393C 'creat':346C 'cross':100C,162C 'cross-ten':99C,161C 'current':197C 'cve':15A,23A 'cwe':30B 'd304214db06e8a4ce9cb41138f199e3bc6ed36f2':189C 'data':67B,103C,119C,164C,396C,508C 'data.id':338C 'data.payload':334C 'default':208C 'deliv':66B,122C,168C 'detach':604C 'diff':275C 'differ':13A 'dispatch':509C 'disposableexecutor':245C 'document':203C,236C 'downstream':402C 'earlier':375C,379C 'ed':634C 'emit':398C 'ensurewebsocket':211C 'event':595C 'ever':336C,577C 'everi':62B,116C,315C 'executor':5A,47B,80B,153C,359C,435C,458C,483C,497C,528C,538C,548C,560C 'field':131C 'find':257C 'first':88B,94B 'frame':68B,120C,397C 'fresh':481C 'gateway':32B,448C 'general':243C 'ghsa':19A 'ghsa-mp66-8ww2-pcwm':18A 'github.com':147C 'github.com/ardatan/graphql-tools':146C 'graphql':1A,27A,34B,37B,50B,107C,135C,566C 'graphql-subscript':33B 'graphql-tool':36B,49B,106C,134C 'graphql-tools':26A 'handler':60B,158C,303C,324C,327C,601C 'id':73B,128C,342C,512C 'impact':550C 'implement':623C 'in-repo':474C 'inconsist':470C 'index.ts:113':228C 'index.ts:35':217C 'input':389C 'insid':313C,484C 'insofar':531C 'instal':300C 'instanc':360C 'iter':591C 'keep':293C 'leak':104C,165C 'legaci':4A,482C,527C,559C 'legacy_ws.data':330C 'legitim':401C 'line':348C,355C,506C,515C 'match':186C 'messag':59B,302C 'metadata':130C 'mp66':20A 'mutabl':225C 'natur':444C 'need':413C,422C 'never':632C 'new':316C 'node':39B 'noth':382C 'npm':40B,138C,140C,182C 'observ':156C,611C 'observer.next':333C 'one':7A,54B,287C,294C,358C,558C 'onmessag':226C,503C 'open':85B,215C 'oper':72B,127C,232C,341C,408C,583C,619C,630C 'ordinari':392C 'output':280C 'overlap':364C 'overwrit':57B,319C 'overwritten':504C 'packag':41B,133C,178C 'packages/loaders/url/src/index.ts':471C,518C 'packages/utils/src/executor.ts':246C 'payload':91B,371C,568C 'pcwm':22A 'per':463C,539C 'pkg':139C 'previous':321C 'privat':565C 'product':523C 'proof':266C 'properti':227C,312C 'protocol':126C 'public':202C 'publish':181C 'rang':251C,253C 'rebuild':536C 'receiv':86B,370C,381C,561C,593C,613C 'recent':368C 'record':254C 'rejectunauthor':290C 'releas':198C 'remot':457C 'replac':114C 'repo':476C 'report':129C 'repositori':145C 'reproduc':194C,269C 'request':464C,486C,540C 'restrict':234C 'result':10A,167C,615C,625C 'return':152C,239C,496C 'reus':46B,212C,362C,461C,498C 'reusabl':244C 'rout':6A 'safe':529C 'schema':44B,450C 'second':75B 'sent':351C 'server':403C,419C 'servic':453C,555C 'shape':445C 'share':3A,53B,111C,434C,492C,502C,557C 'show':285C,468C 'simultan':587C 'sinc':628C 'singl':224C,231C,310C 'single-operation-on':230C 'socket':216C,221C,308C 'sourc':175C,187C 'start':353C,427C,633C 'starv':588C 'state':298C,493C 'stitch':43B,449C 'stitched-schema':42B 'stop':98B,592C 'stream':97B 'subscrib':63B,117C,155C,314C,322C,597C 'subscript':8A,35B,83B,102C,166C,317C,345C,365C,430C,440C,567C 'summari':291C 'surfac':201C 'surround':466C 'tag':395C 'taint':388C 'tarbal':183C 'tenant':101C,163C 'therefor':318C 'tool':2A,38B,51B,108C,136C,143C 'tools':28A 'type':240C 'underst':263C 'unrel':288C 'unsaf':501C 'urlload':477C 'use':77B 'user':552C,563C 'valu':132C 'verbatim':569C 'verifi':173C 'version':179C,250C 'victim':406C,438C,572C,585C 'vulner':159C 'websocket':55B,295C 'websocket.onmessage':112C,157C,311C 'without':69B,123C,335C,510C,576C,605C 'would':612C 'wrapper':478C,534C 'wrong':171C 'yes':210C","nicknames":[],"mitigations":[],"who_affected":"Applications that build one legacy WebSocket executor with `buildWSLegacyExecutor` and reuse it for overlapping subscriptions from different callers, such as gateways and stitched schemas. Consumers that create a new executor for each operation are not affected by the cross-caller leak. Whether other consumers are affected is unknown.","tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-28T21:05:07+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-mp66-8ww2-pcwm","url":"https://github.com/advisories/GHSA-mp66-8ww2-pcwm","severity":"medium","published_at":"2026-10-28T21:05:07+00:00","modified_at":"2026-10-28T21:05:09+00:00","first_seen_at":"2026-10-28T21:07:44+00:00","fetched_at":"2026-10-28T21:07:44+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-118666","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-118666","severity":null,"published_at":"2026-10-28T21:16:56+00:00","modified_at":"2026-10-28T21:16:56+00:00","first_seen_at":"2026-10-28T21:35:45+00:00","fetched_at":"2026-10-28T21:35:45+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}