{"id":"CVE-2026-123810","kind":"vulnerability","aliases":["CVE-2026-123810","GHSA-pqh8-p93p-2rx7"],"related":[],"title":"@dynatrace-oss/dynatrace-mcp-server has a DQL injection via parameters not documented as DQL","description":"### Summary\nA DQL injection vulnerability in several read tools lets a caller bypass the tools' documented field-scope, time-window, and display caps by injecting DQL pipeline stages through parameters typed as identifiers.\n\n### Details\nSeveral tools interpolate caller-supplied parameters directly into DQL query strings without quoting or escaping. The affected parameters are documented in their Zod schemas as identifiers or constrained shorthand (such as `\"24h\"` timeframe values or Kubernetes UIDs) - not as DQL expressions. The interpolation lets a caller break out of string literals, append arbitrary DQL pipeline stages, and use `//` line comments (documented in the [Dynatrace DQL language reference](https://docs.dynatrace.com/docs/discover-dynatrace/platform/grail/dynatrace-query-language/dql-reference)) to discard the rest of the intended query.\n\nThe marginal-privilege ceiling is low because the operator's token also exposes `execute_dql` with full DQL access. What the injection grants is the ability to bypass the affected tools' contract: the `readOnlyHint: true` annotation that MCP clients may treat as a signal to auto-approve, the per-tool field selection (e.g., `| fields id, name, type`), the display caps (`maxProblemsToDisplay`, `maxVulnerabilitiesToDisplay`, `maxEntitiesToDisplay`), and the time-window bounds.\n\nThe vulnerable interpolations are:\n\n| File | Line | Parameter | Interpolation |\n|------|------|-----------|---------------|\n| `src/capabilities/find-monitored-entity-by-name.ts` | 23 | `entityNames[]` | `` `fetch ${entityType} \\| search \"*${entityNames.join('*\" OR \"*')}*\" \\| fieldsAdd entity.type \\| expand tags` `` |\n| `src/capabilities/find-monitored-entity-by-name.ts` | 41 | `entityNames[]` | `` `smartscapeNodes \"*\" \\| search \"*${entityNames.join('*\" OR \"*')}*\" \\| fields id, name, type` `` |\n| `src/capabilities/list-problems.ts` | 27 | `timeframe` | `` `fetch dt.davis.problems, from: now()-${timeframe}, to: now()` `` |\n| `src/capabilities/list-vulnerabilities.ts` | 19 | `timeframe` | `` `fetch security.events, from: now()-${timeframe}, to: now()` `` |\n| `src/capabilities/list-exceptions.ts` | 11 | `timeframe` | `` `fetch user.events, from: now()-${timeframe}, to: now()` `` |\n| `src/capabilities/get-events-for-cluster.ts` | 20 | `timeframe` | `` `fetch events, from: now()-${timeframe}, to: now()` `` |\n| `src/capabilities/get-events-for-cluster.ts` | 27 | `clusterId`, `kubernetesEntityId` | `` `\\| filter k8s.cluster.uid == \"${clusterId}\" or dt.entity.kubernetes_cluster == \"${kubernetesEntityId}\"` `` |\n\nAll Zod schemas for these parameters use `z.string()` or `z.array(z.string())` with no pattern validation.\n\n### PoC\n**`clusterId` - quote-and-comment break-out.** With `clusterId = 'x\" or 1==1 //'` the constructed query becomes:\n\n```\n| filter k8s.cluster.uid == \"x\" or 1==1 //\" or dt.entity.kubernetes_cluster == \"\"\n```\n\nThe first `\"` closes the string literal, `or 1==1` neutralises the filter to match every row, and `//` discards the rest of the line including the `kubernetesEntityId` guard.\n\n**`entityNames` - pipeline-stage injection.** With `entityNames = ['svc\" | fields id, name, tags //']` the constructed smartscape query becomes:\n\n```\nsmartscapeNodes \"*\" | search \"*svc\" | fields id, name, tags //*\" | fields id, name, type\n```\n\nAfter the `//` line comment, the effective query is `smartscapeNodes \"*\" | search \"*svc\" | fields id, name, tags`. The original `| fields id, name, type` stage is suppressed and replaced with the attacker's field selection - the tool returns whatever field set the attacker requests (including ones not in the tool's documented output contract).\n\n**`timeframe` - prefix injection.** With `timeframe = '30d, to: now() | fieldsAdd internal_secret //'` the list-problems query becomes:\n\n```\nfetch dt.davis.problems, from: now()-30d, to: now() | fieldsAdd internal_secret //, to: now()\n| filter isNull(dt.davis.is_duplicate) OR not(dt.davis.is_duplicate)\n...\n```\n\nA new pipeline stage is injected before the tool's intended `| filter`, and the rest of the query is commented out.\n\nThe server's own `verify_dql` tool can be used to confirm any specific injection payload parses as valid DQL.\n\n### Impact\n- A caller (typically via prompt injection of an LLM that has access to the affected tools) can bypass the tools' field-scope, time-window, and display caps.\n- The affected tools are annotated `readOnlyHint: true`, which some MCP clients treat as a signal to auto-approve. The injection turns a \"safe\" read tool into an arbitrary-DQL surface.\n- No new data access beyond what `execute_dql` already provides - the marginal impact is the auto-approval pathway and the broken tool contract, not privilege escalation.","summary":"Several read tools in the Dynatrace MCP server paste caller-supplied parameters straight into DQL queries without escaping. A caller, often an LLM steered by prompt injection, can break out of the query and bypass field selection, time windows and display caps.","severity":"medium","cvss":4.3,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@dynatrace-oss/dynatrace-mcp-server","fixed":["2.1.1"],"affected":"< 2.1.1","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["ai-agents","ai-tooling","cwe-89","cwe-943","dynatrace","mcp","npm"],"not_affected_if":["not-using-mcp-server","version-2.1.1-or-later"],"how_to_check":"- Check the installed version: `npm ls @dynatrace-oss/dynatrace-mcp-server`\n- Versions below `2.1.1` are affected per the product data.","fix":"- `@dynatrace-oss/dynatrace-mcp-server`: move to `2.1.1`","status":"open","published_at":"2026-07-31T15:56:12+00:00","first_seen_at":"2026-11-09T21:04:01+00:00","triaged_at":"2026-11-09T21:04:18+00:00","updated_at":"2026-11-09T21:04:18+00:00","category":"ai-tooling","confidence":0.7,"triage":{"at":"2026-11-09T21:04:18Z","model":"claude-haiku-5-5","output":{"fix":"- `@dynatrace-oss/dynatrace-mcp-server`: move to `2.1.1`","reach":"network","title":"Dynatrace MCP server: DQL injection bypasses read-tool limits and field scopes","impact":"medium","summary":"Several read tools in the Dynatrace MCP server paste caller-supplied parameters straight into DQL queries without escaping. A caller, often an LLM steered by prompt injection, can break out of the query and bypass field selection, time windows and display caps.","audience":"developers","category":"ai-tooling","nicknames":[],"who_cares":["mcp","dynatrace","ai-agents","npm"],"confidence":0.7,"fix_status":"available","needs_auth":"yes","worst_case":"caller bypasses read limits and runs arbitrary DQL through the token","analyst_note":"","how_to_check":"- Check the installed version: `npm ls @dynatrace-oss/dynatrace-mcp-server`\n- Versions below `2.1.1` are affected per the product data.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["version-2.1.1-or-later","not-using-mcp-server"],"who_is_affected":"Teams that run the Dynatrace MCP server so an AI agent can query their Dynatrace tenant are exposed, especially if an LLM reads untrusted input and its MCP client auto-approves tools marked read-only. The sources say the attacker gains no data beyond what the token's execute_dql tool already grants, so the impact is mainly the broken read-only contract. Teams not using this MCP server can skip it.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"unknown","severity_if_unknown":"unknown"},"cost_usd":0.000943,"input_tokens":6493,"output_tokens":588,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-123810':17A,25A '-2026':16A,24A '-30':525C '-89':37B '-943':39B '/docs/discover-dynatrace/platform/grail/dynatrace-query-language/dql-reference))':192C '/dynatrace-mcp-server':4A,29A '1':383C,384C,393C,394C,405C,406C '11':325C '19':315C '20':335C '23':282C '24h':154C '27':305C,345C '2rx7':22A '30d':509C '41':294C 'abil':227C 'access':220C 'affect':139C,231C 'agent':32B 'ai':31B,34B 'ai-ag':30B 'ai-tool':33B 'also':213C 'annot':237C 'append':174C 'approv':249C 'arbitrari':175C 'attack':481C,492C 'auto':248C 'auto-approv':247C 'b':548C 'becom':388C,441C,520C 'bound':272C 'break':72B,169C,377C 'break-out':376C 'bypass':78B,98C,229C 'caller':53B,63B,97C,126C,168C 'caller-suppli':52B,125C 'cap':85B,110C,263C 'ceil':205C 'client':240C 'close':400C 'cluster':353C,397C 'clusterid':346C,350C,371C,380C 'comment':182C,375C,456C 'constrain':150C 'construct':386C,438C 'contract':233C,503C 'cve':15A,23A 'cwe':36B,38B 'd':526C 'detail':121C 'direct':129C 'discard':194C,415C 'display':84B,109C,262C 'docs.dynatrace.com':191C 'docs.dynatrace.com/docs/discover-dynatrace/platform/grail/dynatrace-query-language/dql-reference))':190C 'document':12A,101C,142C,183C,501C 'dql':7A,14A,58B,88C,113C,131C,162C,176C,187C,216C,219C 'dt.davis.is':536C,540C 'dt.davis.problems':308C,522C 'dt.entity.kubernetes':352C,396C 'duplic':537C,541C 'dynatrac':2A,40B,48B,186C 'dynatrace':27A 'dynatrace-oss':1A,26A 'e.g':256C 'effect':458C 'entity.type':290C 'entitynam':283C,295C,425C,431C 'entitynames.join':287C,298C 'entitytyp':285C 'escap':61B,137C 'event':338C 'everi':412C 'execut':215C 'expand':291C 'expos':214C 'express':163C 'fetch':284C,307C,317C,327C,337C,521C 'field':79B,103C,254C,257C,300C,433C,445C,449C,464C,470C,483C,489C 'field-scop':102C 'fieldsadd':289C,512C,529C 'file':277C 'filter':348C,389C,409C,534C 'first':399C 'full':218C 'ghsa':19A 'ghsa-pqh8-p93p-2rx7':18A 'grant':224C 'guard':424C 'id':258C,301C,434C,446C,450C,465C,471C 'identifi':120C,148C 'includ':421C,494C 'inject':8A,70B,89C,112C,223C,429C,506C,547C 'intend':199C 'intern':513C,530C 'interpol':124C,165C,275C,280C 'isnul':535C 'k8s.cluster.uid':349C,390C 'kubernet':158C 'kubernetesentityid':347C,354C,423C 'languag':188C 'let':95C,166C 'line':181C,278C,420C,455C 'list':517C 'list-problem':516C 'liter':173C,403C 'llm':66B 'low':207C 'margin':203C 'marginal-privileg':202C 'match':411C 'maxentitiestodisplay':266C 'maxproblemstodisplay':264C 'maxvulnerabilitiestodisplay':265C 'may':241C 'mcp':41B,49B,239C 'name':259C,302C,435C,447C,451C,466C,472C 'neutralis':407C 'new':543C 'npm':42B 'often':64B 'one':495C 'oper':210C 'origin':469C 'oss':3A,28A 'output':502C 'p93p':21A 'paramet':10A,55B,117C,128C,140C,279C,360C 'past':51B 'pattern':368C 'per':252C 'per-tool':251C 'pipelin':114C,177C,427C,544C 'pipeline-stag':426C 'poc':370C 'pqh8':20A 'prefix':505C 'privileg':204C 'problem':518C 'prompt':69B 'queri':59B,76B,132C,200C,387C,440C,459C,519C 'quot':135C,373C 'quote-and-com':372C 'read':44B,93C 'readonlyhint':235C 'refer':189C 'replac':478C 'request':493C 'rest':196C,417C 'return':487C 'row':413C 'schema':146C,357C 'scope':104C 'search':286C,297C,443C,462C 'secret':514C,531C 'security.events':318C 'select':80B,255C,484C 'server':50B 'set':490C 'sever':43B,92C,122C 'shorthand':151C 'signal':245C 'smartscap':439C 'smartscapenod':296C,442C,461C 'src/capabilities/find-monitored-entity-by-name.ts':281C,293C 'src/capabilities/get-events-for-cluster.ts':334C,344C 'src/capabilities/list-exceptions.ts':324C 'src/capabilities/list-problems.ts':304C 'src/capabilities/list-vulnerabilities.ts':314C 'stage':115C,178C,428C,474C,545C 'steer':67B 'straight':56B 'string':133C,172C,402C 'summari':86C 'suppli':54B,127C 'suppress':476C 'svc':432C,444C,463C 'tag':292C,436C,448C,467C 'time':81B,106C,270C 'time-window':105C,269C 'timefram':155C,306C,311C,316C,321C,326C,331C,336C,341C,504C,508C 'token':212C 'tool':35B,45B,94C,100C,123C,232C,253C,486C,499C 'treat':242C 'true':236C 'type':118C,260C,303C,452C,473C 'uid':159C 'use':180C,361C 'user.events':328C 'valid':369C 'valu':156C 'via':9A 'vulner':90C,274C 'whatev':488C 'window':82B,107C,271C 'without':60B,134C 'x':381C,391C 'z.array':364C 'z.string':362C,365C 'zod':145C,356C","nicknames":[],"mitigations":[],"who_affected":"Teams that run the Dynatrace MCP server so an AI agent can query their Dynatrace tenant are exposed, especially if an LLM reads untrusted input and its MCP client auto-approves tools marked read-only. The sources say the attacker gains no data beyond what the token's execute_dql tool already grants, so the impact is mainly the broken read-only contract. Teams not using this MCP server can skip it.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["dynatracemcpserver"],"ptokens":["dynatrace","mcp","oss","server"],"labels":["aiagents","aitooling","cwe89","cwe943","dynatrace","mcp","medium","npm"],"campaign_type":null,"search_a":"'-123810':17A,25A '-2026':16A,24A '/dynatrace-mcp-server':4A,29A '2rx7':22A 'cve':15A,23A 'document':12A 'dql':7A,14A 'dynatrac':2A 'dynatrace':27A 'dynatrace-oss':1A,26A 'ghsa':19A 'ghsa-pqh8-p93p-2rx7':18A 'inject':8A 'oss':3A,28A 'p93p':21A 'paramet':10A 'pqh8':20A 'via':9A","search_b":"'-89':37B '-943':39B 'agent':32B 'ai':31B,34B 'ai-ag':30B 'ai-tool':33B 'break':72B 'bypass':78B 'caller':53B,63B 'caller-suppli':52B 'cap':85B 'cwe':36B,38B 'display':84B 'dql':58B 'dynatrac':40B,48B 'escap':61B 'field':79B 'inject':70B 'llm':66B 'mcp':41B,49B 'npm':42B 'often':64B 'paramet':55B 'past':51B 'prompt':69B 'queri':59B,76B 'read':44B 'select':80B 'server':50B 'sever':43B 'steer':67B 'straight':56B 'suppli':54B 'time':81B 'tool':35B,45B 'window':82B 'without':60B","search_c":"'-30':525C '/docs/discover-dynatrace/platform/grail/dynatrace-query-language/dql-reference))':192C '1':383C,384C,393C,394C,405C,406C '11':325C '19':315C '20':335C '23':282C '24h':154C '27':305C,345C '30d':509C '41':294C 'abil':227C 'access':220C 'affect':139C,231C 'also':213C 'annot':237C 'append':174C 'approv':249C 'arbitrari':175C 'attack':481C,492C 'auto':248C 'auto-approv':247C 'b':548C 'becom':388C,441C,520C 'bound':272C 'break':169C,377C 'break-out':376C 'bypass':98C,229C 'caller':97C,126C,168C 'caller-suppli':125C 'cap':110C,263C 'ceil':205C 'client':240C 'close':400C 'cluster':353C,397C 'clusterid':346C,350C,371C,380C 'comment':182C,375C,456C 'constrain':150C 'construct':386C,438C 'contract':233C,503C 'd':526C 'detail':121C 'direct':129C 'discard':194C,415C 'display':109C,262C 'docs.dynatrace.com':191C 'docs.dynatrace.com/docs/discover-dynatrace/platform/grail/dynatrace-query-language/dql-reference))':190C 'document':101C,142C,183C,501C 'dql':88C,113C,131C,162C,176C,187C,216C,219C 'dt.davis.is':536C,540C 'dt.davis.problems':308C,522C 'dt.entity.kubernetes':352C,396C 'duplic':537C,541C 'dynatrac':186C 'e.g':256C 'effect':458C 'entity.type':290C 'entitynam':283C,295C,425C,431C 'entitynames.join':287C,298C 'entitytyp':285C 'escap':137C 'event':338C 'everi':412C 'execut':215C 'expand':291C 'expos':214C 'express':163C 'fetch':284C,307C,317C,327C,337C,521C 'field':103C,254C,257C,300C,433C,445C,449C,464C,470C,483C,489C 'field-scop':102C 'fieldsadd':289C,512C,529C 'file':277C 'filter':348C,389C,409C,534C 'first':399C 'full':218C 'grant':224C 'guard':424C 'id':258C,301C,434C,446C,450C,465C,471C 'identifi':120C,148C 'includ':421C,494C 'inject':89C,112C,223C,429C,506C,547C 'intend':199C 'intern':513C,530C 'interpol':124C,165C,275C,280C 'isnul':535C 'k8s.cluster.uid':349C,390C 'kubernet':158C 'kubernetesentityid':347C,354C,423C 'languag':188C 'let':95C,166C 'line':181C,278C,420C,455C 'list':517C 'list-problem':516C 'liter':173C,403C 'low':207C 'margin':203C 'marginal-privileg':202C 'match':411C 'maxentitiestodisplay':266C 'maxproblemstodisplay':264C 'maxvulnerabilitiestodisplay':265C 'may':241C 'mcp':239C 'name':259C,302C,435C,447C,451C,466C,472C 'neutralis':407C 'new':543C 'one':495C 'oper':210C 'origin':469C 'output':502C 'paramet':117C,128C,140C,279C,360C 'pattern':368C 'per':252C 'per-tool':251C 'pipelin':114C,177C,427C,544C 'pipeline-stag':426C 'poc':370C 'prefix':505C 'privileg':204C 'problem':518C 'queri':132C,200C,387C,440C,459C,519C 'quot':135C,373C 'quote-and-com':372C 'read':93C 'readonlyhint':235C 'refer':189C 'replac':478C 'request':493C 'rest':196C,417C 'return':487C 'row':413C 'schema':146C,357C 'scope':104C 'search':286C,297C,443C,462C 'secret':514C,531C 'security.events':318C 'select':255C,484C 'set':490C 'sever':92C,122C 'shorthand':151C 'signal':245C 'smartscap':439C 'smartscapenod':296C,442C,461C 'src/capabilities/find-monitored-entity-by-name.ts':281C,293C 'src/capabilities/get-events-for-cluster.ts':334C,344C 'src/capabilities/list-exceptions.ts':324C 'src/capabilities/list-problems.ts':304C 'src/capabilities/list-vulnerabilities.ts':314C 'stage':115C,178C,428C,474C,545C 'string':133C,172C,402C 'summari':86C 'suppli':127C 'suppress':476C 'svc':432C,444C,463C 'tag':292C,436C,448C,467C 'time':106C,270C 'time-window':105C,269C 'timefram':155C,306C,311C,316C,321C,326C,331C,336C,341C,504C,508C 'token':212C 'tool':94C,100C,123C,232C,253C,486C,499C 'treat':242C 'true':236C 'type':118C,260C,303C,452C,473C 'uid':159C 'use':180C,361C 'user.events':328C 'valid':369C 'valu':156C 'vulner':90C,274C 'whatev':488C 'window':107C,271C 'without':134C 'x':381C,391C 'z.array':364C 'z.string':362C,365C 'zod':145C,356C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-07-31T15:56:12+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-pqh8-p93p-2rx7","url":"https://github.com/advisories/GHSA-pqh8-p93p-2rx7","severity":"medium","published_at":"2026-07-31T15:56:12+00:00","modified_at":"2026-11-09T21:00:00+00:00","first_seen_at":"2026-11-09T21:04:01+00:00","fetched_at":"2026-11-09T21:08:45+00:00","note":null}],"documents":[{"id":109879,"url":"https://api.github.com/repos/dynatrace-oss/dynatrace-mcp/security-advisories/GHSA-pqh8-p93p-2rx7","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":109880,"url":"https://github.com/dynatrace-oss/dynatrace-mcp/security/advisories/GHSA-pqh8-p93p-2rx7","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":109881,"url":"https://github.com/dynatrace-oss/dynatrace-mcp/pull/562","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":109882,"url":"https://github.com/dynatrace-oss/dynatrace-mcp/commit/15d3546c0618ffbaeaeca477337e08e92f2151bc","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":109883,"url":"https://github.com/dynatrace-oss/dynatrace-mcp/releases/tag/v2.1.1","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}