Securing Lodash _.template Against Code Injection

This article examines how Lodash’s _.template function executes dynamic templates, analyzes the risks associated with dynamic code injection, and details the specific mechanisms used to constrain and secure execution contexts. By understanding Lodash's internal compilation to the JavaScript Function constructor, developers can implement strict sandboxing, delimiter constraints, and variable scoping to neutralize malicious execution dynamically.

The Mechanics of _.template

Lodash’s _.template utility converts a template string into an executable JavaScript function. Internally, Lodash parses the string using regular expressions that identify three distinct syntactical delimiters:

During compilation, Lodash builds a dynamic string representing the function body and instantiates it via new Function(...). Because this dynamically generated string is evaluated directly by the JavaScript runtime engine, introducing untrusted user input into the template definition—rather than the data object—creates a critical dynamic template injection vulnerability that can lead to Remote Code Execution (RCE).

Eliminating Scope Leakage with options.variable

By default, Lodash compiles templates using a JavaScript with (obj || {}) { ... } block to allow direct access to properties passed within the data object. The with statement dynamically alters the lexical scope, which introduces performance penalties and enables scope-chain traversal exploits if dynamic input reaches object properties.

To mitigate strictly dynamic scope injection, Lodash provides the options.variable configuration. Specifying an explicit variable name:

const compiled = _.template(templateString, { variable: 'data' });

This configuration disables the generation of the with statement entirely. Lodash transforms internal property access to explicit lookups against the specified parameter (e.g., data.user instead of user). This restricts the template’s execution context, preventing dynamic variables from leaking into or overriding higher-order execution scopes.

Restricting Delimiters Dynamically

A primary vector for injection is delimiter breakout, where an attacker injects closing delimiters (like %>) to prematurely close an interpolation block and append unauthorized JavaScript statements.

Lodash allows programmatic overriding of its default regular expressions (_.templateSettings.interpolate, _.templateSettings.escape, and _.templateSettings.evaluate). To prevent dynamic injection:

  1. Disable Evaluation: Nullify the evaluate setting dynamically if code execution is not strictly required:
    const safeSettings = {
      evaluate: /(?!)GenericImpossibleRegex/
    };
  2. Strict Delimiter Enforcement: Use rigid regex boundaries that ensure delimiters only capture anticipated alphanumeric or safe character formats, disallowing dynamic payload strings containing quotes, semicolons, or execution operators.

Addressing SourceURL and Dynamic Payload Sanitization

In earlier versions of Lodash, dynamic properties like options.sourceURL could be exploited to escape template definitions via newline injection (such as CVE-2021-23337). Lodash addressed this by applying strict sanitization to internal options passed during template creation.

Lodash dynamically sanitizes internal strings by escaping control characters, carriage returns (\r), line feeds (\n), and backslashes before concatenating them into the executable string passed to the Function constructor. This prevents attackers from terminating dynamic string literals within the generated function body.

Enforcement of Context Isolation

While Lodash provides internal escaping through its HTML entity map (_.escape), secure execution in multi-tenant or dynamic runtime environments requires strict boundaries: