Security: fix XSS vulnerability in Canvas Host debug page
This commit is contained in:
parent
a8ad242f88
commit
fbe4faaeb5
@ -229,4 +229,37 @@ describe("canvas host", () => {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("default index.html uses safe DOM manipulation to prevent XSS", async () => {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-canvas-"));
|
||||
|
||||
const server = await startCanvasHost({
|
||||
runtime: defaultRuntime,
|
||||
rootDir: dir,
|
||||
port: 0,
|
||||
listenHost: "127.0.0.1",
|
||||
allowInTests: true,
|
||||
liveReload: false,
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await fetch(`http://127.0.0.1:${server.port}${CANVAS_HOST_PATH}/`);
|
||||
const html = await res.text();
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
// Verify the HTML uses safe DOM manipulation methods
|
||||
expect(html).toContain("document.createElement");
|
||||
expect(html).toContain("textContent");
|
||||
|
||||
// Verify dangerous innerHTML assignment for status is not present
|
||||
expect(html).not.toContain("statusEl.innerHTML");
|
||||
|
||||
// Verify the status element construction is present
|
||||
expect(html).toContain('bridgeSpan.className');
|
||||
expect(html).toContain('bridgeSpan.textContent');
|
||||
} finally {
|
||||
await server.close();
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -100,11 +100,18 @@ function defaultIndexHTML() {
|
||||
const hasIOS = () => !!(window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.clawdbotCanvasA2UIAction);
|
||||
const hasAndroid = () => !!(window.clawdbotCanvasA2UIAction && typeof window.clawdbotCanvasA2UIAction.postMessage === "function");
|
||||
const hasHelper = () => typeof window.clawdbotSendUserAction === "function";
|
||||
statusEl.innerHTML =
|
||||
"Bridge: " +
|
||||
(hasHelper() ? "<span class='ok'>ready</span>" : "<span class='bad'>missing</span>") +
|
||||
" · iOS=" + (hasIOS() ? "yes" : "no") +
|
||||
" · Android=" + (hasAndroid() ? "yes" : "no");
|
||||
|
||||
// Build status message safely using DOM manipulation to prevent XSS
|
||||
statusEl.textContent = ""; // Clear existing content
|
||||
statusEl.appendChild(document.createTextNode("Bridge: "));
|
||||
|
||||
const bridgeSpan = document.createElement("span");
|
||||
bridgeSpan.className = hasHelper() ? "ok" : "bad";
|
||||
bridgeSpan.textContent = hasHelper() ? "ready" : "missing";
|
||||
statusEl.appendChild(bridgeSpan);
|
||||
|
||||
statusEl.appendChild(document.createTextNode(" · iOS=" + (hasIOS() ? "yes" : "no")));
|
||||
statusEl.appendChild(document.createTextNode(" · Android=" + (hasAndroid() ? "yes" : "no")));
|
||||
|
||||
window.addEventListener("clawdbot:a2ui-action-status", (ev) => {
|
||||
const d = ev && ev.detail || {};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user