Handlebars.js enforces automated HTML-escaping by default to protect outputs, but relies heavily on secure developer configuration for runtime compilation, context scoping, and prototype access. Developers must assume that unvalidated dynamic inputs, unconstrained partial names, and permissive compiler flags can introduce severe cross-site scripting, prototype pollution, or code execution vulnerabilities. Security-sensitive surfaces such as template compilation, helper registration, and AST processing must fail closed by explicitly restricting default privileges.
Essential implementation rules
Restrict prototype access options during compilation and rendering
Set allowProtoMethodsByDefault and allowProtoPropertiesByDefault to false in runtime options when rendering templates, and explicitly whitelist only approved properties to prevent prototype pollution.
Validate dynamic partial names against an allowlist
Never render user-controlled dynamic partial names without confirming they match an explicitly registered and trusted partial map. Use the runtime-only package require('handlebars/runtime') to disable fallback compilation.
Restrict partial context scope using explicitPartialContext
Set explicitPartialContext to true in compiler options to prevent partials from implicitly inheriting higher-scope parent evaluation context across boundaries.
Use precompiled templates and partials in runtime-only environments
Compile templates and partials during the build step using precompilation tools and register the resulting functions before rendering, since runtime-only bundles do not include the compiler.
Safely serialize identifiers in custom compiler extensions
Format name identifiers using JSON.stringify rather than raw string concatenation when extending Handlebars.JavaScriptCompiler or overriding nameLookup to prevent code injection.
Enforce strict compilation mode for required data bindings
Pass { strict: true } to Handlebars.compile() when rendering templates that require explicit data bindings, causing missing path references or undefined objects to throw an exception instead of silently failing.
Reject raw unvalidated AST objects during compilation
Pass raw string templates directly into Handlebars.compile() and reject untrusted pre-parsed AST structures to prevent parser bypass and arbitrary code execution.
Sanitize helper output and enforce HTML escaping
Sanitize dynamic helper inputs using Handlebars.Utils.escapeExpression before wrapping in Handlebars.SafeString, keep noEscape set to false, and ensure programmatically built MustacheStatement nodes include escaped: true.
Restrict helper execution using knownHelpersOnly
Pass knownHelpersOnly: true and define an explicit knownHelpers whitelist during compilation of untrusted templates to ensure the compiler rejects unknown helper expressions.
handlebars-js: All Security Cards
Approximately 2,619 tokens
On this card
Category: access control
Restrict prototype access options during template compilation and rendering
Use when
Rendering templates with context objects where prototype properties and methods must be restricted to prevent unauthorized access or prototype pollution.
Secure rules
Rule 1: Keep prototype access restrictions enabled during template execution and explicitly whitelist individual properties if required.
Ensure that allowProtoMethodsByDefault and allowProtoPropertiesByDefault are set to false in the runtime options when rendering templates, and explicitly whitelist only approved properties.
Validate dynamic partial names against an allowlist before execution
Use when
Rendering templates that use dynamic partial names resolved at runtime.
Secure rules
Rule 1: Validate dynamic partial names that come from external input
Dynamic partial syntax such as {{> (lookup . "key")}} must never render user-controlled names without first confirming that the name is one of the partials you have explicitly registered. Unresolved names already throw an exception in the v4.7.9 runtime, and past RCE vulnerabilities were exploitable when attackers supplied crafted values through dynamic lookups.
When the name can vary:
Build with the runtime-only package (require('handlebars/runtime')) so fallback compilation is disabled.
Keep a private map of trusted, pre-registered partials and return a safe default if the requested name is not in that map.
// runtime-only build – no compile() fallbackconst Handlebars = require('handlebars/runtime');// trusted, pre-compiled partialsconst PARTIALS = { user_card: require('./templates/user_card.js'), item_card: require('./templates/item_card.js')};for (const [name, fn] of Object.entries(PARTIALS)) { Handlebars.registerPartial(name, fn);}// helper returns a partial name only if it is trustedHandlebars.registerHelper('safePartialName', (name) => Object.prototype.hasOwnProperty.call(PARTIALS, name) ? name : 'user_card');// main template was precompiled with `{{> (safePartialName which)}}`const render = require('./templates/main.js');console.log(render({ which: 'item_card', item: 'Book', name: 'Alice' }));
Category: boundary control
Restrict Partial Context Scope Using explicitPartialContext
Use when
Compiling Handlebars templates that utilize partials and require isolation of context data across boundaries.
Secure rules
Rule 1: Enable explicitPartialContext during template compilation to prevent partials from inheriting the full parent evaluation context implicitly.
When compiling templates that render partials, set explicitPartialContext to true in the compiler options. This enforces boundary control by forcing partials to receive only explicitly passed parameters or data rather than automatically inheriting higher-scope properties.
Use precompiled partials in runtime-only Handlebars environments
Use when
Building and running applications using the standalone Handlebars runtime environment where dynamic template compilation is restricted.
Secure rules
Rule 1: Precompile templates and partials for runtime-only builds
The lightweight handlebars/runtime bundle does not include the compiler. Any attempt to render a string template or partial triggers an error, so compile every template and partial during your build step (e.g., with the handlebars CLI) and register the resulting functions before rendering.
import Handlebars from 'handlebars/runtime';// functions generated at build time by `handlebars --precompile`import mainTpl from './templates/main.hbs.js';import headerPartial from './templates/header.hbs.js';// make the precompiled partial availableHandlebars.registerPartial('header', headerPartial);// render safely at runtime-onlyconsole.log(mainTpl({ title: 'Hello Handlebars' }));
Category: injection
Safely serialize variable names and validate configuration options in custom compiler extensions
Use when
Extending Handlebars.JavaScriptCompiler or programmatically invoking the precompiler API with custom configuration options.
Secure rules
Rule 1: Serialize name identifiers safely using JSON.stringify or standard compiler invocation methods when customizing code generation.
When extending Handlebars.JavaScriptCompiler to override nameLookup, format name identifiers safely using JSON.stringify rather than raw string concatenation to prevent code injection during template compilation.
Rule 2: Ensure precompiler options conform to standard identifier formats and use strict validation for module paths.
When programmatically calling the precompiler via cli(opts), ensure options such as namespace, commonjs, and handlebarPath contain trusted inputs or conform strictly to standard identifier formats to prevent arbitrary JavaScript code injection.
Enforce strict compilation mode for required data bindings
Use when
Compiling Handlebars templates that require explicit data bindings and property validations.
Secure rules
Rule 1: Configure Handlebars with strict compilation mode to reject missing input properties.
Pass { strict: true } to Handlebars.compile() when rendering templates that require explicit data bindings. This enforces required field enforcement by causing property lookups, child path accesses, missing data references, and undefined context objects to throw an exception instead of silently rendering empty string values.
Validate AST Node Types and Reject Raw AST Objects during Compilation
Use when
Compiling template strings or processing custom abstract syntax trees in Handlebars to prevent parser bypass and AST type confusion.
Secure rules
Rule 1: Always compile trusted raw template strings and reject untrusted pre-parsed AST objects.
Pass raw string templates directly into Handlebars.compile() rather than supplying unvalidated or attacker-controlled AST structures, which bypasses parser validation and can lead to arbitrary code execution.
Rule 2: Verify AST structural integrity and node properties using built-in traversal methods.
When writing custom visitors or compiler extensions, use methods like acceptRequired, acceptArray, and acceptKey to enforce that node types and required properties exist before processing.
Use standard double-brace expressions and escape untrusted template output
Use when
Rendering dynamic or user-supplied data within Handlebars templates and custom helpers to prevent cross-site scripting.
Secure rules
Rule 1: Sanitize helper output before returning SafeString instances.
Sanitize dynamic input using Handlebars.Utils.escapeExpression before wrapping content in Handlebars.SafeString or outputting raw HTML from custom helpers.
Rule 2: Set escaped: true on MustacheStatement nodes you build so dynamic values are HTML-escaped
When you create or modify a Handlebars AST programmatically, include escaped: true on each MustacheStatement that should use the engine’s default HTML-escaping. If you omit the property—or set it to false—the compiler treats the expression as unescaped and emits raw output.
Rule 4: Keep noEscapefalse so {{…}} output stays HTML-escaped
The compiler option noEscape turns off Handlebars’ automatic HTML escaping for all mustache expressions in a template. Leave it at its default false when rendering data you don’t fully trust.
Restrict helper execution using knownHelpersOnly when compiling templates
Use when
When compiling untrusted template strings to enforce strict compilation boundaries and prevent dynamic resolution of unlisted functions.
Secure rules
Rule 1: Pass options.knownHelpersOnly set to true alongside an explicit options.knownHelpers whitelist during template compilation.
Pass knownHelpersOnly: true and list allowed helper names in knownHelpers when compiling untrusted template definitions to ensure the compiler rejects any unknown helper expressions at compile time.
Restrict prototype access options during template compilation and rendering
Approximately 192 tokens
Use when
Rendering templates with context objects where prototype properties and methods must be restricted to prevent unauthorized access or prototype pollution.
Secure rules
Rule 1: Keep prototype access restrictions enabled during template execution and explicitly whitelist individual properties if required.
Ensure that allowProtoMethodsByDefault and allowProtoPropertiesByDefault are set to false in the runtime options when rendering templates, and explicitly whitelist only approved properties.
Validate dynamic partial names against an allowlist before execution
Approximately 398 tokens
Use when
Rendering templates that use dynamic partial names resolved at runtime.
Secure rules
Rule 1: Validate dynamic partial names that come from external input
Dynamic partial syntax such as {{> (lookup . "key")}} must never render user-controlled names without first confirming that the name is one of the partials you have explicitly registered. Unresolved names already throw an exception in the v4.7.9 runtime, and past RCE vulnerabilities were exploitable when attackers supplied crafted values through dynamic lookups.
When the name can vary:
Build with the runtime-only package (require('handlebars/runtime')) so fallback compilation is disabled.
Keep a private map of trusted, pre-registered partials and return a safe default if the requested name is not in that map.
// runtime-only build – no compile() fallbackconst Handlebars = require('handlebars/runtime');// trusted, pre-compiled partialsconst PARTIALS = { user_card: require('./templates/user_card.js'), item_card: require('./templates/item_card.js')};for (const [name, fn] of Object.entries(PARTIALS)) { Handlebars.registerPartial(name, fn);}// helper returns a partial name only if it is trustedHandlebars.registerHelper('safePartialName', (name) => Object.prototype.hasOwnProperty.call(PARTIALS, name) ? name : 'user_card');// main template was precompiled with `{{> (safePartialName which)}}`const render = require('./templates/main.js');console.log(render({ which: 'item_card', item: 'Book', name: 'Alice' }));
Restrict Partial Context Scope Using explicitPartialContext
Approximately 184 tokens
Use when
Compiling Handlebars templates that utilize partials and require isolation of context data across boundaries.
Secure rules
Rule 1: Enable explicitPartialContext during template compilation to prevent partials from inheriting the full parent evaluation context implicitly.
When compiling templates that render partials, set explicitPartialContext to true in the compiler options. This enforces boundary control by forcing partials to receive only explicitly passed parameters or data rather than automatically inheriting higher-scope properties.
Use precompiled partials in runtime-only Handlebars environments
Approximately 239 tokens
Use when
Building and running applications using the standalone Handlebars runtime environment where dynamic template compilation is restricted.
Secure rules
Rule 1: Precompile templates and partials for runtime-only builds
The lightweight handlebars/runtime bundle does not include the compiler. Any attempt to render a string template or partial triggers an error, so compile every template and partial during your build step (e.g., with the handlebars CLI) and register the resulting functions before rendering.
import Handlebars from 'handlebars/runtime';// functions generated at build time by `handlebars --precompile`import mainTpl from './templates/main.hbs.js';import headerPartial from './templates/header.hbs.js';// make the precompiled partial availableHandlebars.registerPartial('header', headerPartial);// render safely at runtime-onlyconsole.log(mainTpl({ title: 'Hello Handlebars' }));
Restrict SafeString Usage to Sanitized HTML Content
Approximately 302 tokens
Use when
When wrapping custom string values to prevent Handlebars from applying automatic HTML escaping.
Secure rules
Rule 1: Use double-brace {{…}} for untrusted data and reserve {{{…}}}/{{& …}} for pre-sanitized HTML
Handlebars v4.7.9 automatically HTML-escapes values rendered with standard double braces. Triple braces ({{{…}}}) or the ampersand form ({{& …}}) bypass this protection and must only be used with content that is already safe to insert as raw HTML.
const Handlebars = require('handlebars');// Escaped output for user-supplied dataconst tpl = Handlebars.compile( '<p>{{userInput}}</p>\n<p>{{{trustedHtml}}}</p>');console.log( tpl({ userInput: '<script>alert("xss")</script>', // will be escaped trustedHtml: '<strong>Hello</strong>' // already sanitized }));// => <p><script>alert("xss")</script></p>// <p><strong>Hello</strong></p>
Safely serialize variable names and validate configuration options in custom compiler extensions
Approximately 377 tokens
Use when
Extending Handlebars.JavaScriptCompiler or programmatically invoking the precompiler API with custom configuration options.
Secure rules
Rule 1: Serialize name identifiers safely using JSON.stringify or standard compiler invocation methods when customizing code generation.
When extending Handlebars.JavaScriptCompiler to override nameLookup, format name identifiers safely using JSON.stringify rather than raw string concatenation to prevent code injection during template compilation.
Rule 2: Ensure precompiler options conform to standard identifier formats and use strict validation for module paths.
When programmatically calling the precompiler via cli(opts), ensure options such as namespace, commonjs, and handlebarPath contain trusted inputs or conform strictly to standard identifier formats to prevent arbitrary JavaScript code injection.
Enforce strict compilation mode for required data bindings
Approximately 216 tokens
Use when
Compiling Handlebars templates that require explicit data bindings and property validations.
Secure rules
Rule 1: Configure Handlebars with strict compilation mode to reject missing input properties.
Pass { strict: true } to Handlebars.compile() when rendering templates that require explicit data bindings. This enforces required field enforcement by causing property lookups, child path accesses, missing data references, and undefined context objects to throw an exception instead of silently rendering empty string values.
Validate AST Node Types and Reject Raw AST Objects during Compilation
Approximately 310 tokens
Use when
Compiling template strings or processing custom abstract syntax trees in Handlebars to prevent parser bypass and AST type confusion.
Secure rules
Rule 1: Always compile trusted raw template strings and reject untrusted pre-parsed AST objects.
Pass raw string templates directly into Handlebars.compile() rather than supplying unvalidated or attacker-controlled AST structures, which bypasses parser validation and can lead to arbitrary code execution.
Rule 2: Verify AST structural integrity and node properties using built-in traversal methods.
When writing custom visitors or compiler extensions, use methods like acceptRequired, acceptArray, and acceptKey to enforce that node types and required properties exist before processing.
Use standard double-brace expressions and escape untrusted template output
Approximately 759 tokens
Use when
Rendering dynamic or user-supplied data within Handlebars templates and custom helpers to prevent cross-site scripting.
Secure rules
Rule 1: Sanitize helper output before returning SafeString instances.
Sanitize dynamic input using Handlebars.Utils.escapeExpression before wrapping content in Handlebars.SafeString or outputting raw HTML from custom helpers.
Rule 2: Set escaped: true on MustacheStatement nodes you build so dynamic values are HTML-escaped
When you create or modify a Handlebars AST programmatically, include escaped: true on each MustacheStatement that should use the engine’s default HTML-escaping. If you omit the property—or set it to false—the compiler treats the expression as unescaped and emits raw output.
Rule 4: Keep noEscapefalse so {{…}} output stays HTML-escaped
The compiler option noEscape turns off Handlebars’ automatic HTML escaping for all mustache expressions in a template. Leave it at its default false when rendering data you don’t fully trust.
Restrict helper execution using knownHelpersOnly when compiling templates
Approximately 190 tokens
Use when
When compiling untrusted template strings to enforce strict compilation boundaries and prevent dynamic resolution of unlisted functions.
Secure rules
Rule 1: Pass options.knownHelpersOnly set to true alongside an explicit options.knownHelpers whitelist during template compilation.
Pass knownHelpersOnly: true and list allowed helper names in knownHelpers when compiling untrusted template definitions to ensure the compiler rejects any unknown helper expressions at compile time.