Files
openclaw/extensions/diffs/src/viewer-client.test.ts
tanshanshan b7f9bf5a5c fix(diffs): replace iconMarkup string with ToolbarIconName enum to el… (#83955)
* fix(diffs): replace iconMarkup string with ToolbarIconName enum to eliminate XSS sink

Replace createToolbarButton's iconMarkup: string parameter with icon: ToolbarIconName,
a union of known icon names. SVG generation moves into a sealed toolbarIconSvg map so
innerHTML only receives compile-time-known strings. The old splitIcon/unifiedIcon/
wrapIcon/backgroundIcon/themeIcon functions are removed; callers now pass icon name
literals instead of raw markup strings.

Closes #83918

* fix(diffs): remove jsdom dependency from viewer-client test

Use source file string analysis instead of jsdom to avoid missing
@types/jsdom declaration error in check-test-types CI job.

* fix(diffs): restore wrap icon arrow segment in ToolbarIconName map

The wrap-on and wrap-off SVG paths were missing the original wrap arrow
segment (M14 6h-4V5h4.5...). Restore the exact original path data and
rebuild the viewer runtime bundle.

* build(diffs): refresh viewer runtime after rebase

---------

Co-authored-by: tanshanshan <tanshanshan@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-05-21 17:39:01 +08:00

61 lines
1.7 KiB
TypeScript

import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const VIEWER_CLIENT_SRC = readFileSync(
new URL("./viewer-client.ts", import.meta.url),
"utf8",
);
const XSS_PATTERNS = ["onerror", "<script", "onclick", "javascript:", "onload"];
describe("createToolbarButton icon safety", () => {
it("toolbarIconSvg map exists and has exactly 8 icon names", () => {
const requiredNames = [
"split",
"unified",
"wrap-on",
"wrap-off",
"background-on",
"background-off",
"theme-dark",
"theme-light",
] as const;
for (const name of requiredNames) {
expect(
VIEWER_CLIENT_SRC.includes(name + ":") || VIEWER_CLIENT_SRC.includes(`"${name}"`),
`icon "${name}" should exist in toolbarIconSvg`,
).toBe(true);
}
});
it("no iconMarkup: string parameter exists", () => {
expect(VIEWER_CLIENT_SRC.includes("iconMarkup: string")).toBe(false);
});
it("innerHTML reads only from toolbarIconSvg lookup", () => {
expect(VIEWER_CLIENT_SRC.includes("button.innerHTML = toolbarIconSvg[params.icon]")).toBe(true);
});
it("SVG strings in toolbarIconSvg contain no XSS patterns", () => {
for (const pattern of XSS_PATTERNS) {
expect(
VIEWER_CLIENT_SRC.includes(pattern),
`source must not contain "${pattern}"`,
).toBe(false);
}
});
it("old icon functions are removed", () => {
const removedFunctions = [
"function splitIcon(",
"function unifiedIcon(",
"function wrapIcon(",
"function backgroundIcon(",
"function themeIcon(",
];
for (const fn of removedFunctions) {
expect(VIEWER_CLIENT_SRC.includes(fn), `"${fn}" should be removed`).toBe(false);
}
});
});