CVE-2026-39412 Overview
A security bypass vulnerability exists in LiquidJS, a Shopify and GitHub Pages compatible template engine written in pure JavaScript. Prior to version 10.25.4, the sort_natural filter bypasses the ownPropertyOnly security option, allowing template authors to extract values of prototype-inherited properties through a sorting side-channel attack. This vulnerability enables information disclosure in applications that rely on ownPropertyOnly: true as a security boundary, particularly affecting multi-tenant template systems where sensitive prototype properties such as API keys and tokens may be exposed.
Critical Impact
Multi-tenant template systems using LiquidJS with the ownPropertyOnly security option may inadvertently expose sensitive prototype properties including API keys and authentication tokens to malicious template authors.
Affected Products
- LiquidJS versions prior to 10.25.4
- Applications using LiquidJS with ownPropertyOnly: true configuration
- Multi-tenant template rendering systems built on LiquidJS
Discovery Timeline
- 2026-04-08 - CVE CVE-2026-39412 published to NVD
- 2026-04-09 - Last updated in NVD database
Technical Details for CVE-2026-39412
Vulnerability Analysis
This information disclosure vulnerability (CWE-200) stems from an incomplete implementation of the ownPropertyOnly security feature in LiquidJS. The sort_natural filter was designed to perform case-insensitive sorting of array elements, but it failed to respect the security boundary established by the ownPropertyOnly configuration option. When enabled, ownPropertyOnly should restrict template access to only properties directly owned by objects, preventing access to inherited prototype properties. However, the sorting mechanism inadvertently exposed prototype chain values through observable sorting behavior, creating a side-channel that attackers could exploit.
The vulnerability is particularly concerning for multi-tenant environments where template authors may be untrusted. By crafting malicious templates that leverage the sort_natural filter, an attacker can infer or extract sensitive values stored in object prototypes, potentially including configuration data, API credentials, and authentication tokens.
Root Cause
The root cause lies in the ScopeObject interface definition and the sorting filter implementation. The original interface typed the index signature as Record<string, any>, which didn't properly account for symbol properties in the prototype chain. Additionally, the sort_natural filter used a comparison function that could access prototype-inherited properties during the sorting operation, bypassing the intended ownPropertyOnly restriction.
The fix required two changes: updating the ScopeObject interface to properly type index signatures with Record<string | number | symbol, any> and modifying the sorting filters to use an orderedCompare utility that respects property ownership boundaries.
Attack Vector
The attack exploits the sorting behavior of the sort_natural filter to leak information about prototype-inherited properties. An attacker with the ability to create or modify LiquidJS templates can craft input that causes the sorting function to compare values from the prototype chain. By observing the resulting sort order, the attacker can deduce the values of sensitive prototype properties through multiple iterations.
The attack requires network access to submit malicious templates but does not require authentication in scenarios where untrusted users can provide template content.
// Security patch in src/context/scope.ts
// Source: https://github.com/harttle/liquidjs/commit/e743da0020d34e2ee547e1cc1a86b58377ebe1ce
import { Drop } from '../drop/drop'
-interface ScopeObject extends Record<string, any> {
+interface ScopeObject extends Record<string | number | symbol, any> {
toLiquid?: () => any;
}
// Security patch in src/filters/array.ts - Updated imports to use orderedCompare
// Source: https://github.com/harttle/liquidjs/commit/e743da0020d34e2ee547e1cc1a86b58377ebe1ce
-import { toArray, argumentsToValue, toValue, stringify, caseInsensitiveCompare, isArray, isNil, last as arrayLast, isArrayLike, toEnumerable } from '../util'
+import { toArray, argumentsToValue, toValue, stringify, caseInsensitiveCompare, orderedCompare, isArray, isNil, last as arrayLast, isArrayLike, toEnumerable } from '../util'
import { arrayIncludes, equals, evalToken, isTruthy } from '../render'
import { Value, FilterImpl } from '../template'
import { Tokenizer } from '../parser'
Detection Methods for CVE-2026-39412
Indicators of Compromise
- Unusual or excessive use of sort_natural filter in user-submitted templates
- Templates containing iterative sorting operations designed to extract information through comparison behavior
- Unexpected data leakage in template rendering logs or outputs
- Reports of sensitive configuration data appearing in rendered template output
Detection Strategies
- Audit LiquidJS templates for suspicious sort_natural and sort filter usage patterns
- Implement template validation that flags potentially malicious sorting constructs
- Monitor template rendering operations for attempts to access prototype properties
- Review application logs for anomalous template execution patterns
Monitoring Recommendations
- Enable verbose logging for template rendering operations in multi-tenant environments
- Implement rate limiting on template submissions to slow down side-channel attacks
- Deploy security monitoring for sensitive configuration data exposure
- Create alerts for templates exhibiting information extraction patterns
How to Mitigate CVE-2026-39412
Immediate Actions Required
- Upgrade LiquidJS to version 10.25.4 or later immediately
- Audit existing templates for malicious use of sort_natural filter
- Review and validate templates from untrusted sources before deployment
- Assess whether sensitive data may have been exposed prior to patching
Patch Information
The vulnerability is fixed in LiquidJS version 10.25.4. The patch modifies the ScopeObject interface in src/context/scope.ts to properly handle symbol properties and updates the array filters in src/filters/array.ts to use the secure orderedCompare utility function that respects the ownPropertyOnly boundary.
For detailed patch information, see the GitHub Security Advisory GHSA-rv5g-f82m-qrvv, the GitHub Pull Request #869, and the GitHub Release v10.25.4.
Workarounds
- Restrict template authoring capabilities to trusted users only until patching is complete
- Implement additional template sandboxing to prevent access to prototype properties
- Review and sanitize all user-submitted templates before rendering
- Consider implementing a template allowlist that restricts available filters
# Configuration example
# Upgrade LiquidJS to the patched version
npm update liquidjs@10.25.4
# Verify the installed version
npm list liquidjs
# For yarn users
yarn upgrade liquidjs@10.25.4
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


