mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-12 04:29:51 +08:00
GET /drawio/<path> serves dist/drawio with a MIME table, a day of caching and nosniff; paths are normalized and never reach WEB-INF or META-INF. The preview embeds /drawio/index.html when the copy exists and DRAWIO_BASE_URL is unset, else the external draw.io as before (and start_session says so). Every /api request must carry the per-process X-Drawio-Token the page gets in its HTML; pages send frame-ancestors 'self' and nosniff.
48 lines
2.0 KiB
TypeScript
48 lines
2.0 KiB
TypeScript
import { readFileSync } from "node:fs"
|
|
import { join } from "node:path"
|
|
import { describe, expect, it } from "vitest"
|
|
|
|
// The MCP preview page, as the server fills it in, with its script run in
|
|
// this document (no session id, so it does not poll)
|
|
const dir = join(process.cwd(), "packages/mcp-server/src/preview")
|
|
const html = readFileSync(join(dir, "index.html"), "utf8")
|
|
.replace("{{CSS}}", "")
|
|
.replace("{{SESSION_BADGE}}", "")
|
|
.replaceAll("{{DISABLED}}", "")
|
|
.replace("{{DRAWIO_URL}}", "about:blank")
|
|
.replace("{{SESSION_JSON}}", '""')
|
|
.replace("{{ORIGIN_JSON}}", '"https://embed.diagrams.net"')
|
|
.replace("{{TOKEN_JSON}}", '"test-token"')
|
|
const scripts = [...html.matchAll(/<script>([\s\S]*?)<\/script>/g)].map((m) =>
|
|
m[1].replace("{{SCRIPT}}", ""),
|
|
)
|
|
const preview = readFileSync(join(dir, "preview.js"), "utf8")
|
|
|
|
function renderHistory(entries: unknown[]): HTMLElement {
|
|
document.body.innerHTML = html.replace(/<script>[\s\S]*?<\/script>/g, "")
|
|
// One scope, as the page's scripts share one; returns its renderHistory
|
|
const run = new Function(
|
|
`${scripts.join("\n")}\n${preview}\nreturn (d) => { historyData = d; renderHistory(); }`,
|
|
)
|
|
run()(entries)
|
|
return document.getElementById("history-grid") as HTMLElement
|
|
}
|
|
|
|
describe("MCP preview History", () => {
|
|
it("never reads a stored thumbnail as HTML", () => {
|
|
const grid = renderHistory([
|
|
{ id: 1, index: 0, svg: 'x" onerror="window.__xss=1' },
|
|
{ id: 2, index: 1, svg: "javascript:window.__xss=2" },
|
|
{ id: 3, index: 2, svg: "data:image/svg+xml;base64,PHN2Zy8+" },
|
|
])
|
|
const images = [...grid.querySelectorAll("img")]
|
|
expect(images.map((i) => i.getAttribute("src"))).toEqual([
|
|
"data:image/svg+xml;base64,PHN2Zy8+",
|
|
])
|
|
expect(grid.querySelector("[onerror]")).toBeNull()
|
|
// Entries without a usable picture show their number
|
|
expect(grid.textContent).toContain("#0")
|
|
expect(grid.textContent).toContain("#1")
|
|
})
|
|
})
|