fix(ui): make mega menus work under strict CSP
Quality / quality (windows-latest) (push) Waiting to run
Quality / quality (ubuntu-latest) (push) Failing after 9m55s

This commit is contained in:
2026-08-24 21:52:25 +05:30
parent 9067e68f69
commit 0618782355
12 changed files with 99 additions and 19 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.8.33",
"version": "0.8.34",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -90,7 +90,7 @@ const CONTROLLER_LOADER = String.raw`
if (!componentControllerPromise) {
componentControllerPromise = new Promise(function (resolve, reject) {
var script = document.createElement("script");
script.src = componentControllerUrl;
script.src = trustedScriptURL(componentControllerUrl);
script.defer = true;
script.onload = resolve;
script.onerror = reject;
+38 -4
View File
@@ -26,6 +26,40 @@ export const REACTIVE_RUNTIME = String.raw`
var behaviorObserver;
var clientModuleCache = new Map();
/*
* Production enables require-trusted-types-for 'script'. Every HTML write
* below uses compiler-emitted template markup (or the explicit data-html
* escape hatch), and controller URLs are generated by the framework. Route
* those known sinks through the policy allowed by the default security
* preset instead of letting the browser reject hydration midway through a
* state update.
*/
var wrnexusTrustedTypesPolicy = null;
if (window.trustedTypes && typeof window.trustedTypes.createPolicy === "function") {
try {
wrnexusTrustedTypesPolicy = window.trustedTypes.createPolicy("wrnexus", {
createHTML: function (value) { return value; },
createScriptURL: function (value) { return value; }
});
} catch (_) {
wrnexusTrustedTypesPolicy = window.trustedTypes.defaultPolicy || null;
}
}
function trustedHTML(value) {
var source = value == null ? "" : String(value);
return wrnexusTrustedTypesPolicy
? wrnexusTrustedTypesPolicy.createHTML(source)
: source;
}
function trustedScriptURL(value) {
var source = value == null ? "" : String(value);
return wrnexusTrustedTypesPolicy
? wrnexusTrustedTypesPolicy.createScriptURL(source)
: source;
}
/*
* Two builtin chains the runtime reaches for constantly. Aliasing them is
* not only shorter: hasOwn keeps prototype keys from reading as data, and
@@ -1818,7 +1852,7 @@ export const REACTIVE_RUNTIME = String.raw`
var next = markup == null ? "" : String(markup);
if (htmlNode.innerHTML !== next) {
htmlNode.innerHTML = next;
htmlNode.innerHTML = trustedHTML(next);
}
});
runHtml();
@@ -2002,7 +2036,7 @@ export const REACTIVE_RUNTIME = String.raw`
}
function clearControlContent() {
if (!rangeEnd) { block.innerHTML = ""; return; }
if (!rangeEnd) { block.innerHTML = trustedHTML(""); return; }
while (block.nextSibling && block.nextSibling !== rangeEnd) {
block.parentNode.removeChild(block.nextSibling);
}
@@ -2010,7 +2044,7 @@ export const REACTIVE_RUNTIME = String.raw`
function appendControlContent(markup, locals) {
var template = document.createElement("template");
template.innerHTML = markup || "";
template.innerHTML = trustedHTML(markup || "");
var fragment = template.content;
var elements = toArray(fragment.childNodes).filter(function (node) {
return node.nodeType === 1;
@@ -2399,7 +2433,7 @@ export const REACTIVE_RUNTIME = String.raw`
var markup = value == null ? "" : String(value);
if (node.innerHTML !== markup) {
node.innerHTML = markup;
node.innerHTML = trustedHTML(markup);
}
});
});
+7
View File
@@ -62,6 +62,13 @@ test("split runtime hydrates a controller only from the controller asset", () =>
expect(win.document.activeElement).toBe(buttons[1]);
});
test("production hydration uses Trusted Types for HTML and controller URLs", () => {
const runtime = getReactiveRuntime();
expect(runtime).toContain('createPolicy("wrnexus"');
expect(runtime).toContain('template.innerHTML = trustedHTML(markup || "")');
expect(runtime).toContain("script.src = trustedScriptURL(componentControllerUrl)");
});
test("a page without controller markers does not request the controller asset", () => {
const win = mount(
`<main data-scope="count: 1"><span>{count}</span></main>`,