Severity: 🟡 Medium (High if templates can carry untrusted content, e.g. a CMS/page-builder)
Location
src/ssr/render.ts:189-197 (new Function() fallback) and :174-187 (dot-path, missing proto guard). Used by the DOM-backed renderer — active whenever a global/configured DOMParser exists (browsers, happy-dom, or configureSSR({ backend: 'dom' })).
Description
evaluateSSR falls back to dynamic code generation for any non-trivial expression:
const fn = new Function(...keys, `return (${trimmed});`);
return fn(...values);
This directly contradicts the module's stated security posture — src/ssr/expression.ts was written specifically to be CSP-safe (no eval/Function) — and re-introduces unsafe-eval on the DOM path. If any part of a template expression is attacker-influenced, this is code execution.
Additionally, the fast dot-notation path (render.ts:174-187) does not call isPrototypePollutionKey, unlike the hardened expression.ts (lookupIdent/safeMember). evaluateSSR('constructor.constructor', ctx) walks the prototype chain and returns the Function constructor — an inconsistent hardening gap and a stepping stone when combined with the new Function fallback.
Suggested fix
Replace evaluateSSR with the CSP-safe evaluateExpression from expression.ts (already used by the pure renderer), eliminating both the new Function fallback and the prototype-pollution gap, and unifying evaluator behaviour across backends.
Filed as part of a full-codebase security & correctness audit.
Severity: 🟡 Medium (High if templates can carry untrusted content, e.g. a CMS/page-builder)
Location
src/ssr/render.ts:189-197(new Function()fallback) and:174-187(dot-path, missing proto guard). Used by the DOM-backed renderer — active whenever a global/configuredDOMParserexists (browsers, happy-dom, orconfigureSSR({ backend: 'dom' })).Description
evaluateSSRfalls back to dynamic code generation for any non-trivial expression:This directly contradicts the module's stated security posture —
src/ssr/expression.tswas written specifically to be CSP-safe (noeval/Function) — and re-introducesunsafe-evalon the DOM path. If any part of a template expression is attacker-influenced, this is code execution.Additionally, the fast dot-notation path (
render.ts:174-187) does not callisPrototypePollutionKey, unlike the hardenedexpression.ts(lookupIdent/safeMember).evaluateSSR('constructor.constructor', ctx)walks the prototype chain and returns theFunctionconstructor — an inconsistent hardening gap and a stepping stone when combined with thenew Functionfallback.Suggested fix
Replace
evaluateSSRwith the CSP-safeevaluateExpressionfromexpression.ts(already used by the pure renderer), eliminating both thenew Functionfallback and the prototype-pollution gap, and unifying evaluator behaviour across backends.Filed as part of a full-codebase security & correctness audit.