Repository navigation
Conversation
There was a problem hiding this comment.
Pull request overview
This PR shifts “untrusted AST” hardening from the parser (parseWithoutProcessing) to the compiler/codegen path, aiming to neutralize type-confusion based code injection by coercing AST-provided values into safe primitives during compilation.
Changes:
- Remove AST value validation from
parseWithoutProcessing(no longer throwsInvalid ASTfor malformed nodes). - Add numeric/boolean-safe opcodes (
pushNumber,pushBoolean) and coercegetContextdepth. - Update security/compiler specs to expect safe handling/coercion rather than parser-time rejection.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| spec/security.js | Adjusts the untrusted-AST regression test to check for neutralization rather than “Invalid AST” rejection. |
| spec/compiler.js | Updates compiler tests to accept malformed AST values and assert safe compilation behavior. |
| lib/handlebars/compiler/javascript-compiler.js | Adds pushNumber / pushBoolean and coerces getContext depth. |
| lib/handlebars/compiler/compiler.js | Sanitizes PathExpression depth/parts, and routes Number/Boolean literals through new opcodes. |
| lib/handlebars/compiler/base.js | Removes AST validation when a pre-parsed Program is passed to parsing entrypoints. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
64b5ab6 to
dbe0494
Compare
|
@jaylinski Here's what Claude has to say about this PR, when I asked it to dig deeper into perf implications:
Before: validateInputAst ran only when the caller passed a pre-parsed AST (minority use). Compiling from a string For a typical template with N path expressions, this adds roughly N × (Number + isFinite + Math.floor + Array.isArray
lib/handlebars/utils.js: Suggested fast-path:
this.opcode('lookupBlockParam', [Number(blockParamId[0]), Number(blockParamId[1])], parts);
In PathExpression the depth is sanitized once; in pushParam it's sanitized again for each param that's itself a
Most of this is micro-cost. The real fix for perf would be:
Recommendation: ask the author to run npm test with time, and a synthetic "compile 10k templates" bench against 4.x Correctness concerns beyond perf Behavioral change for BooleanLiteral Old code: pushLiteral(bool.value) — for a valid AST, value is a boolean and renders as true/false. For the valid case this is equivalent (boolean → number/string interpolation in generated code produces the same).
NumberLiteral → NaN Number('{},{})) + ...') → NaN. pushLiteral(NaN) emits the literal NaN into generated JS. Safe (no code injection), but sanitizeDepth clamps negative to 0, old code rejected Old mitigation threw on depth: -1. New mitigation clamps to 0 and happily compiles — {{../foo}} with a forged depth: Summary
Would push the author to: (a) add a compile-path bench, (b) fix the sanitizeParts fast path, (c) drop the Number() |
|
@kibertoad Thanks, will have a look! |
Instead of validating the AST in the parser, fix the compiler instead by handling the types in a safe way.
dbe0494 to
6a9a4d4
Compare
OverviewPR #2143 reworks the AST-injection hardening (GHSA-2w6w-674q-4c4q and friends). Instead of validateInputAst throwing on a malformed pre-parsed AST in parseWithoutProcessing, it removes that validator entirely and coerces untrusted values to safe primitives at the compiler boundary: Did it address the previous concerns? Mostly no on perf. The prior review's top actionable item (sanitizeParts fast-path) and item (c) (drop the Number() wraps) were not implemented. The depth-clamping concerns from Copilot were addressed (by routing through sanitizeDepth, which floors and clamps), and the pushParam injection gap was genuinely closed. Findings (most severe first)
|
|
Hi @jaylinski have you fixed and merged? It seems like the RCE is still alive on 4.7.9 |
Will do soon. |
|
Superseded by #2185. |
Instead of validating the AST in the parser, fix the compiler instead by handling the types in a safe way.
master