Severity: 🟡 Medium (correctness — build-breaking)
Location
src/view/compiler/expression.ts:120-128 (scanStringLiteral), surfaced at :380-386; emitted at src/view/compiler/emit.ts:35.
Description
scanStringLiteral returns i + 1 unconditionally and never verifies the closing quote was found:
const scanStringLiteral = (src, start) => {
const quote = src[start];
let i = start + 1;
while (i < src.length && src[i] !== quote) {
if (src[i] === '\\') i += 1;
i += 1;
}
return i + 1; // returns past end-of-input if quote never closed
};
An unterminated string literal in a directive expression is therefore copied verbatim and rewrite returns normally, so compileExpression reports { ok: true, code } with a syntactically broken body. Because emitModule writes every expression into one module, a single malformed expression throws at import time and takes down all precompiled expressions in that file — and unlike the runtime path, there is no per-expression try/catch fallback.
Repro: template <p bq-text="'oops"></p> compiles to:
registerCompiledExpressions({
"'oops": (__bq_ctx) => ('oops), // SyntaxError → whole module fails to import
});
The same class of bug produces invalid emitted code for malformed numeric literals (e.g. 1ex), since scanNumericLiteral (:135-153) will also happily consume an invalid run.
Suggested fix
In scanStringLiteral, detect reaching end-of-input without the matching quote and throw new Bail('unterminated string literal') so the build falls back to the runtime evaluator for that expression. Add a post-scan validity check for numeric literals, or wrap emitted expressions so one bad entry cannot break the whole module.
Filed as part of a full-codebase security & correctness audit.
Severity: 🟡 Medium (correctness — build-breaking)
Location
src/view/compiler/expression.ts:120-128(scanStringLiteral), surfaced at:380-386; emitted atsrc/view/compiler/emit.ts:35.Description
scanStringLiteralreturnsi + 1unconditionally and never verifies the closing quote was found:An unterminated string literal in a directive expression is therefore copied verbatim and
rewritereturns normally, socompileExpressionreports{ ok: true, code }with a syntactically broken body. BecauseemitModulewrites every expression into one module, a single malformed expression throws at import time and takes down all precompiled expressions in that file — and unlike the runtime path, there is no per-expressiontry/catchfallback.Repro: template
<p bq-text="'oops"></p>compiles to:The same class of bug produces invalid emitted code for malformed numeric literals (e.g.
1ex), sincescanNumericLiteral(:135-153) will also happily consume an invalid run.Suggested fix
In
scanStringLiteral, detect reaching end-of-input without the matching quote andthrow new Bail('unterminated string literal')so the build falls back to the runtime evaluator for that expression. Add a post-scan validity check for numeric literals, or wrap emitted expressions so one bad entry cannot break the whole module.Filed as part of a full-codebase security & correctness audit.