GHSA-78x9-fhhx-v2g6
CKAN MCP Server: Cache-key canonicalization collision enables cache confusion / poisoning
Packages
| Ecosystem | Package | Affected | Fixed in |
|---|---|---|---|
| npm | @aborruso/ckan-mcp-server | < 0.4.112 | 0.4.112 |
Details
As written in the advisory (Markdown source, shown as plain text).
## Summary
The response cache derives its key from an ambiguous string serialization of the request parameters. `canonicalizeParams` joins sorted `${key}=${value}` pairs with `&` and does not escape `&`, `=`, or the `|` field separators used in `buildCacheKey`. Two **different** logical parameter sets can therefore serialize to the **same** key and share one cache entry. Because the cached value is whatever the upstream returned for whichever request populated the entry first, an attacker can prime a colliding key so a victim's distinct query (same `server_url`) is served the attacker's cached response.
## Affected code
```js
// src/utils/cache.ts
export function canonicalizeParams(params) {
const keys = Object.keys(params).sort();
const pairs = [];
for (const key of keys) {
const value = params[key];
if (value === undefined || value === null) continue;
const serialized = typeof value === "object" ? JSON.stringify(value) : String(value);
pairs.push(`${key}=${serialized}`); // value not escaped
}
return pairs.join("&"); // '&' delimiter, injectable
}
export async function buildCacheKey(serverUrl, action, params) {
const raw = `${serverUrl}|${action}|${canonicalizeParams(params)}`; // '|' also unescaped
return sha1Hex(raw);
}
```
Confirmed collisions (identical key):
- `{ q: "budget", rows: 10 }` ≡ `{ q: "budget&rows=10" }` → both canonicalize to `q=budget&rows=10`
- `{ filters: { a: "b" } }` ≡ `{ filters: '{"a":"b"}' }` → both canonicalize to `filters={"a":"b"}` (object-vs-string ambiguity)
An attacker can reproduce **any** target canonical string by injecting it into the alphabetically-first parameter, so the collision is general, not incidental.
## Impact
- **Cache poisoning / confusion.** On a shared cache (caching is enabled by
default; the Cloudflare Workers deployment uses the shared `caches.default`, and
a Node HTTP instance shares one in-process LRU across all clients), an attacker
primes a colliding entry so that another client's genuinely different query
receives the attacker-chosen response for the same portal.
- **Integrity of results.** Victims receive data for a query they did not make
(wrong dataset list, wrong record set), undermining trust in tool output.
- **Chains with indirect prompt injection (advisory #07).** The attacker's
colliding request can be one whose upstream response surfaces an
attacker-controlled dataset (with malicious `notes`/`title`); the victim's
benign query then serves that poisoned content to the model — delivering prompt
injection via the cache, without the victim ever querying the malicious dataset.
Confidentiality impact is low (same-portal public data); the primary damage is
integrity. `AC:H` reflects the need for caching to be enabled and a shared
instance plus priming before the victim's request populates the entry.
## Proof of concept
`poc/cache-collision-poc.mjs` primes a single-param request and shows a victim's
distinct two-param request being served the attacker-primed entry:
```
attacker canonical : q=budget&rows=10
victim canonical : q=budget&rows=10
same cache key : true
victim served from cache: true
victim RECEIVED : RESULT_FOR({"q":"budget&rows=10"})
victim EXPECTED : RESULT_FOR({"q":"budget","rows":10})
```
## Remediation
- Build the cache key from an unambiguous, injection-proof encoding: hash a
structured, canonical JSON (with typed values) or percent-encode/escape each key
and value before joining, and use a separator that cannot appear in the encoded
fields. Include a type tag so `{a:{...}}` (object) and `{a:"..."}` (string)
never coincide.
- Consider partitioning the cache per client/tenant on shared deployments so one
client cannot influence another's entries.
References
External links from the advisory. They lead to third-party sites.
- https://github.com/ondata/ckan-mcp-server/security/advisories/GHSA-78x9-fhhx-v2g6
- https://nvd.nist.gov/vuln/detail/CVE-2026-73846
- https://github.com/ondata/ckan-mcp-server/commit/8e1522f9bbfa1f3b21550f17887f60f133e24151
- https://github.com/ondata/ckan-mcp-server
- https://github.com/ondata/ckan-mcp-server/releases/tag/v0.4.112