Fix production dynamic-page modules and trusted email previews
Quality / quality (ubuntu-latest) (push) Canceled after 0s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
Clintchiz committed 2026-10-01 19:47:16 +05:30
1 parent bccbf1f4c9
commit 646814d6df
9 files changed
+149 -9

No files matched your search

+5 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/block-editor",
"version": "0.1.18",
"version": "0.1.19",
"type": "module",
"description": "Hydrated accessible editor runtime for WRNexus block documents.",
"exports": {
@@ -25,11 +25,14 @@
"@wrnexus/blocks": "workspace:*",
"@wrnexus/dnd": "workspace:*",
"@wrnexus/reactive": "workspace:*",
"codemirror": "6.0.2"
"codemirror": "6.0.2",
"dompurify": "^3.4.16"
},
"devDependencies": {
"@types/bun": "^1.4.0",
"@types/jsdom": "^30.0.0",
"happy-dom": "^20.11.2",
"jsdom": "^30.1.1",
"typescript": "^6.0.3"
}
}
+1
View File
@@ -10,3 +10,4 @@ export * from "./image-preview.ts";
export * from "./announcements.ts";
export * from "./kit.ts";
export * from "./source-editor.ts";
export { setBuilderPreview, builderPreviewHTML } from "./preview-html.ts";
+2 -1
View File
@@ -1,8 +1,9 @@
import { builderPreviewHTML } from "./preview-html.ts";
/** Updates the existing sandbox document without navigating/reloading its iframe. */
export function updateBuilderCanvas(frame: HTMLIFrameElement, html: string): void {
const canvas = frame.contentDocument;
if (!canvas?.body) return;
const parsed = new DOMParser().parseFromString(html, "text/html");
const parsed = new DOMParser().parseFromString(builderPreviewHTML(html, frame.ownerDocument), "text/html");
const scrolling = canvas.scrollingElement;
const top = scrolling?.scrollTop ?? 0, left = scrolling?.scrollLeft ?? 0;
canvas.head.replaceChildren(...Array.from(parsed.head.childNodes).map(node => canvas.importNode(node, true)));
+25
View File
@@ -0,0 +1,25 @@
import createDOMPurify from "dompurify";
const sanitizers = new WeakMap<object, ReturnType<typeof createDOMPurify>>();
/** Sanitize generated previews in the sink's realm, including Trusted Types. */
export function builderPreviewHTML(html: string, doc: Document = document): string {
const realm = doc.defaultView;
if (!realm) throw new Error("Preview document has no browser realm");
let sanitizer = sanitizers.get(realm);
if (!sanitizer) {
sanitizer = createDOMPurify(realm);
sanitizers.set(realm, sanitizer);
}
return sanitizer.sanitize(html, {
WHOLE_DOCUMENT: true,
RETURN_TRUSTED_TYPE: true,
FORBID_TAGS: ["script", "iframe", "object", "embed", "form", "base"],
}) as unknown as string;
}
/** Only script-free sandboxed frames may receive generated email previews. */
export function setBuilderPreview(frame: HTMLIFrameElement, html: string): void {
if (!frame.hasAttribute("sandbox") || frame.sandbox.contains("allow-scripts"))
throw new Error("Email previews require a script-free sandbox");
frame.srcdoc = builderPreviewHTML(html, frame.ownerDocument);
}
@@ -0,0 +1,13 @@
import { expect, test } from "bun:test";
import { JSDOM } from "jsdom";
import { builderPreviewHTML } from "../src/preview-html.ts";
test("preview sanitization preserves email layout and editor markers but removes executable content", () => {
const { window } = new JSDOM("");
const result = String(builderPreviewHTML('<html><head><style>td{padding:12px}</style></head><body><table data-block-id="one"><tr><td><img src="https://example.com/a.png" onerror="alert(1)"><a href="javascript:alert(1)">Hello</a></td></tr></table><script>alert(1)</script><iframe src="https://example.com"></iframe></body></html>', window.document as unknown as Document));
expect(result).toContain('data-block-id="one"');
expect(result).toContain('<table');
expect(result).toContain('<style>');
expect(result).not.toMatch(/onerror|javascript:|<script|<iframe/);
window.close();
});