34 lines
1.7 KiB
JavaScript
34 lines
1.7 KiB
JavaScript
// 可独立测试的 HTML 预览安全策略。脚本和 HTTPS 外部资源可运行,满足图表、
|
||
// 地图、WebGL 等通用产物;iframe 保持 opaque origin,不能触达 zcbot 宿主权限。
|
||
// 使用同源静态宿主页 + postMessage,而非 iframe.srcdoc / blob URL:部分 WebView
|
||
// 对 srcdoc 支持不完整,原生壳的 URL 白名单又常会误拦 blob: 子 frame。
|
||
export const HTML_PREVIEW_CSP = "default-src 'none'; " +
|
||
"script-src 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' https:; " +
|
||
"style-src 'unsafe-inline' https:; img-src data: blob: https:; " +
|
||
"media-src data: blob: https:; font-src data: https:; connect-src https:; " +
|
||
"worker-src blob:; child-src blob: https:; frame-src https:; manifest-src https:; " +
|
||
"object-src 'none'; form-action 'none'; base-uri 'none'";
|
||
export const HTML_PREVIEW_SANDBOX = "allow-scripts";
|
||
export const HTML_PREVIEW_HOST = "/static/html_preview_host.html";
|
||
|
||
export function htmlPreviewDocument(text) {
|
||
const meta = `<meta http-equiv="Content-Security-Policy" content="${HTML_PREVIEW_CSP}">`;
|
||
const source = String(text || "");
|
||
if (/<head(?:\s[^>]*)?>/i.test(source)) {
|
||
return source.replace(/<head(?:\s[^>]*)?>/i, (head) => head + meta);
|
||
}
|
||
return `<head>${meta}</head>${source}`;
|
||
}
|
||
|
||
export function configureHtmlPreviewFrame(frame, text, title = "HTML 文件预览") {
|
||
frame.title = title;
|
||
frame.setAttribute("sandbox", HTML_PREVIEW_SANDBOX);
|
||
frame.referrerPolicy = "no-referrer";
|
||
const document = htmlPreviewDocument(text);
|
||
frame.addEventListener("load", () => {
|
||
frame.contentWindow.postMessage({ type: "zcbot-html-preview", document }, "*");
|
||
}, { once: true });
|
||
frame.src = HTML_PREVIEW_HOST;
|
||
return frame;
|
||
}
|