release: WRNexusJS 0.7.0

This commit is contained in:
2026-08-01 10:04:42 +05:30
parent c54144f2e4
commit 87507edf59
207 changed files with 12607 additions and 679 deletions
+37 -3
View File
@@ -77,6 +77,38 @@ function isHtmlBooleanAttribute(name: string): boolean {
return HTML_BOOLEAN_ATTRIBUTES.has(name.toLowerCase());
}
const URL_ATTRIBUTES = new Set([
"href",
"src",
"action",
"formaction",
"poster",
"cite",
"background",
"xlink:href",
]);
function stripAsciiControlAndSpace(value: string): string {
let result = "";
for (const character of value) {
if (character.charCodeAt(0) > 0x20) result += character;
}
return result;
}
function sanitizeUrlAttribute(value: string): string {
const compact = stripAsciiControlAndSpace(value.trim());
const lower = compact.toLowerCase();
if (/^(?:javascript|vbscript|file):/.test(lower)) return "about:blank";
if (/^data:(?!image\/(?:png|gif|jpeg|webp|avif);)/.test(lower)) return "about:blank";
return value;
}
function safeAttributeValue(name: string, value: string): string {
if (!URL_ATTRIBUTES.has(name.toLowerCase()) || value.includes("{")) return value;
return sanitizeUrlAttribute(value);
}
/** Escape a value placed inside a double-quoted HTML attribute. */
function attrEscape(value: string): string {
return value
@@ -110,7 +142,9 @@ function renderAttr(attr: Attr): string {
case "csrText":
return "";
default:
return attr.boolean ? ` ${attr.name}` : ` ${attr.name}="${attrEscape(attr.value)}"`;
return attr.boolean
? ` ${attr.name}`
: ` ${attr.name}="${attrEscape(safeAttributeValue(attr.name, attr.value))}"`;
}
}
@@ -176,7 +210,7 @@ function renderAttrs(
const initial = reactiveAttrValue(attr.value, reactive);
if (initial === null) return base;
const marker = JSON.stringify([attr.name, attr.value]);
return ` ${attr.name}="${attrEscape(initial)}" data-wrn-bind-${bindIndex++}="${attrEscape(marker)}"`;
return ` ${attr.name}="${attrEscape(URL_ATTRIBUTES.has(attr.name.toLowerCase()) ? sanitizeUrlAttribute(initial) : initial)}" data-wrn-bind-${bindIndex++}="${attrEscape(marker)}"`;
})
.join("");
return csrId ? `${rendered} data-wrnexus-csr="${attrEscape(csrId)}"` : rendered;
@@ -2148,7 +2182,7 @@ function renderPageComponentAttr(attr: Attr, dynamicExpressions: string[]): stri
const expression = wholeAttributeExpression(attr.value);
if (!expression) {
return ` ${attr.name}="${attrEscape(attr.value)}"`;
return ` ${attr.name}="${attrEscape(safeAttributeValue(attr.name, attr.value))}"`;
}
dynamicExpressions.push(`\${__wrnexusPropAttr(${expression})}`);