Both mdNode implementations (agent UI app.js, dashboard common.js) assigned marked.parse() output straight to innerHTML with no sanitizer. marked v5+ dropped its built-in sanitize option, and there was no DOMPurify anywhere in frontend/, so markdown containing raw HTML/script tags rendered live in the browser. Both sinks receive untrusted input in practice: the agent UI's mdNode renders recv tool_result bodies, assistant prose, and send/ask/answer payloads sourced from peer agents and matrix-relayed messages (the documented prompt-injection adversary); the dashboard's mdNode renders agent-authored .md files served verbatim by GET /api/state-file (the endpoint validates path, not content). Since the per-agent UI and dashboard are same-origin behind the gateway with operator-authority endpoints (approve/spawn/rebuild/destroy/answer-question), injected script would run with the operator's session. Fix: DOMPurify.sanitize() the marked.parse() output at both sinks before assigning to innerHTML. Added dompurify as a dependency to both the agent and dashboard npm workspaces, recomputed npmDepsHash in nix/frontend.nix for the updated lockfile. Also corrected docs/web-ui/shape.md, which claimed the markdown-rendering path was XSS-safe by construction the same way the text-node-based linkify path is — it isn't; it's safe because it's sanitized. CSP hardening for the dashboard (no unsafe-inline) is a separate, larger backend change (response headers in hive-c0re) and is left as a fast-follow rather than folded into this fix.
15 lines
402 B
JSON
15 lines
402 B
JSON
{
|
|
"name": "@hive/dashboard",
|
|
"version": "0.0.0",
|
|
"private": true,
|
|
"description": "hive-c0re dashboard SPA. Bundled by esbuild into a static dist; served by the hive-c0re Rust binary at runtime via tower_http::ServeDir.",
|
|
"type": "module",
|
|
"scripts": {
|
|
"build": "node ./build.mjs"
|
|
},
|
|
"dependencies": {
|
|
"@hive/shared": "*",
|
|
"dompurify": "^3.2.4",
|
|
"marked": "18.0.4"
|
|
}
|
|
}
|