GHSA-VQFF-R82H-9CRC
Vulnerability from github – Published: 2026-10-16 19:23 – Updated: 2026-10-16 19:23Summary
The MCP Apps UI resource (an interactive DataStore table rendered in the host's webview/iframe) communicates with its parent via postMessage. Its inbound message listener performs no event.origin validation, and its outbound messages are sent with target origin '*'. Any window/frame able to reach the document can inject data into the table and interfere with the MCP UI handshake, and the UI's own JSON-RPC messages are broadcast without an origin restriction.
Affected code
src/resources/datastore-table-ui.ts (inline HTML):
window.addEventListener('message', function (event) {
var msg = event.data; // no check of event.origin
if (!msg || typeof msg !== 'object') return;
if (msg.id !== undefined && _pending[msg.id]) { ... resolve/reject pending MCP request ... }
...
if (data && (data.fields || data.records)) initTable(data); // renders attacker-supplied data
});
// outbound:
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params || {} }, '*');
mcpNot: window.parent.postMessage({ jsonrpc:'2.0', method:method, params:params||{} }, '*');
There is no allowlist of accepted origins for inbound messages and no fixed target origin for outbound messages.
Impact
- Content spoofing. A frame that can
postMessageto the UI can supply arbitraryfields/records, causing the table to render fabricated rows and links (e.g. a phishing__link_url) that appear to come from the queried portal. (Values are HTML-escaped, so this is display spoofing, not script injection — no XSS.) - Handshake/UX interference. Because inbound messages matching a pending
idresolve/reject the UI's MCP requests, an attacker frame can spoof responses to theui/initializehandshake or otherwise disrupt the panel. - Outbound leakage. Posting to
'*'means the UI's JSON-RPC messages are delivered to whatever the parent origin is; if the resource is ever embedded by a context other than the trusted host, those messages leak to it.
Exploitability depends on the host embedding model (in a correct MCP Apps host the parent is the trusted host, which limits who can message the frame), so this is a hardening / defense-in-depth issue rather than a directly remotely-triggerable one — hence Low.
Proof of concept
poc/verify-no-origin-check.mjs— static confirmation that the listener has no origin check and outbound uses'*':
[x] validates event.origin : NO
[x] outgoing postMessage targetOrigin : '*' (any)
RESULT: VULNERABLE
poc/parent-spoof-poc.html— a page that embeds the UI and injects fabricated rows cross-origin with no handshake; the UI renders them.
Remediation
- Validate
event.originagainst the expected host origin on every inbound message; drop messages from other origins. - Send outbound
postMessagewith an explicit target origin (the host origin established at initialize time) instead of'*'. - Optionally, bind to a nonce/channel established during
ui/initializeso only the negotiated host can drive the panel.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@aborruso/ckan-mcp-server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.4.112"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-76894"
],
"database_specific": {
"cwe_ids": [
"CWE-345",
"CWE-940"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-16T19:23:48Z",
"nvd_published_at": null,
"severity": "LOW"
},
"details": "## Summary\n\nThe MCP Apps UI resource (an interactive DataStore table rendered in the host\u0027s webview/iframe) communicates with its parent via `postMessage`. Its inbound `message` listener performs no `event.origin` validation, and its outbound messages are sent with target origin `\u0027*\u0027`. Any window/frame able to reach the document can inject data into the table and interfere with the MCP UI handshake, and the UI\u0027s own JSON-RPC messages are broadcast without an origin restriction.\n\n## Affected code\n\n`src/resources/datastore-table-ui.ts` (inline HTML):\n\n```js\nwindow.addEventListener(\u0027message\u0027, function (event) {\n var msg = event.data; // no check of event.origin\n if (!msg || typeof msg !== \u0027object\u0027) return;\n if (msg.id !== undefined \u0026\u0026 _pending[msg.id]) { ... resolve/reject pending MCP request ... }\n ...\n if (data \u0026\u0026 (data.fields || data.records)) initTable(data); // renders attacker-supplied data\n});\n\n// outbound:\nwindow.parent.postMessage({ jsonrpc: \u00272.0\u0027, id: id, method: method, params: params || {} }, \u0027*\u0027);\nmcpNot: window.parent.postMessage({ jsonrpc:\u00272.0\u0027, method:method, params:params||{} }, \u0027*\u0027);\n```\n\nThere is no allowlist of accepted origins for inbound messages and no fixed target origin for outbound messages.\n\n## Impact\n\n- **Content spoofing.** A frame that can `postMessage` to the UI can supply\n arbitrary `fields`/`records`, causing the table to render fabricated rows and\n links (e.g. a phishing `__link_url`) that appear to come from the queried\n portal. (Values are HTML-escaped, so this is display spoofing, not script\n injection \u2014 no XSS.)\n- **Handshake/UX interference.** Because inbound messages matching a pending `id`\n resolve/reject the UI\u0027s MCP requests, an attacker frame can spoof responses to\n the `ui/initialize` handshake or otherwise disrupt the panel.\n- **Outbound leakage.** Posting to `\u0027*\u0027` means the UI\u0027s JSON-RPC messages are\n delivered to whatever the parent origin is; if the resource is ever embedded by\n a context other than the trusted host, those messages leak to it.\n\nExploitability depends on the host embedding model (in a correct MCP Apps host the\nparent is the trusted host, which limits who can message the frame), so this is a\nhardening / defense-in-depth issue rather than a directly remotely-triggerable\none \u2014 hence Low.\n\n## Proof of concept\n\n- `poc/verify-no-origin-check.mjs` \u2014 static confirmation that the listener has no\n origin check and outbound uses `\u0027*\u0027`:\n\n ```\n [x] validates event.origin : NO\n [x] outgoing postMessage targetOrigin : \u0027*\u0027 (any)\n RESULT: VULNERABLE\n ```\n\n- `poc/parent-spoof-poc.html` \u2014 a page that embeds the UI and injects fabricated\n rows cross-origin with no handshake; the UI renders them.\n\n## Remediation\n\n- Validate `event.origin` against the expected host origin on every inbound\n message; drop messages from other origins.\n- Send outbound `postMessage` with an explicit target origin (the host origin\n established at initialize time) instead of `\u0027*\u0027`.\n- Optionally, bind to a nonce/channel established during `ui/initialize` so only\n the negotiated host can drive the panel.",
"id": "GHSA-vqff-r82h-9crc",
"modified": "2026-10-16T19:23:48Z",
"published": "2026-10-16T19:23:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ondata/ckan-mcp-server/security/advisories/GHSA-vqff-r82h-9crc"
},
{
"type": "PACKAGE",
"url": "https://github.com/ondata/ckan-mcp-server"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "ckan-mcp-server: DataStore Table UI resource does not validate postMessage origin (in) and posts to targetOrigin \"*\" (out)"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.