MEDIUM 5.3

CVE-2026-44646: LiquidJS Context.spawn() Silent Security Bypass in Template Rendering

LiquidJS, a JavaScript template engine compatible with Shopify and GitHub Pages, has a flaw in how it handles security settings when rendering partial templates using the {% render %} tag. When a developer configures LiquidJS with a permissive default setting but then tries to tighten security for specific untrusted template content, that tightened security is silently ignored for any partials rendered within it. This allows an attacker to access properties from the JavaScript prototype chain—essentially reading unintended internal object data—even when the developer believed they had locked down access.

Source data · NVD / CISA · public domain

CVSS
3.1 · 5.3 MEDIUM · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Weaknesses (CWE)
CWE-693
Affected products
0 configuration(s)
Published / Modified
2026-06-17 / 2026-06-22

NVD description (verbatim)

LiquidJS is a Shopify/GitHub Pages compatible template engine written in pure JavaScript. In versions 10.25.7 and below, Context.spawn() creates a child Context for the {% render %} tag but does not propagate the parent context's resolved ownPropertyOnly value, resulting in a silent bypass. The new context re-derives ownPropertyOnly from opts.ownPropertyOnly (the instance-level option), silently discarding any RenderOptions.ownPropertyOnly override that was supplied to parseAndRender(). As a result, a developer who runs a Liquid instance with the backwards-compatible ownPropertyOnly:false and then locks down an untrusted render with parseAndRender(..., { ownPropertyOnly: true }) still leaks prototype-chain properties from inside any {% render %} partial. This is a distinct exploit surface from the previously identified array-filter variants (where, reject, group_by, find, find_index, has) — the underlying root cause in Context.spawn() is shared, but {% render %} is a separately reachable sink that needs no filter usage. This issue has been fixed in version 10.26.0.

4 reference(s) · View on NVD →

SEC.co analysis · AI-assisted, reviewed against source

Technical summary

CVE-2026-44646 involves improper context inheritance in LiquidJS versions 10.25.7 and below. The Context.spawn() method, invoked during {% render %} tag processing, fails to propagate the resolved ownPropertyOnly setting from the parent context to the child context. Instead, it re-derives ownPropertyOnly exclusively from the instance-level opts.ownPropertyOnly configuration, discarding any RenderOptions.ownPropertyOnly override passed to parseAndRender(). This creates a bypass of per-call security constraints: a developer running a Liquid engine with backwards-compatible ownPropertyOnly:false can attempt to isolate untrusted templates via parseAndRender(..., { ownPropertyOnly: true }), yet prototype-chain properties remain accessible within {% render %} partials. The vulnerability is rooted in Context.spawn() but manifests as a distinct attack vector independent of the array-filter variants (where, reject, group_by, find, find_index, has) that were previously disclosed.

Business impact

Organizations using LiquidJS to process untrusted templates face information disclosure risk. If a deployment relies on per-template security locks (ownPropertyOnly: true in parseAndRender calls) to sandbox user-supplied or third-party Liquid templates, those locks fail silently within {% render %} partials. An attacker crafting a template can extract sensitive data from the JavaScript runtime's prototype chain—potentially including configuration objects, internal library properties, or other unintended application state. The silent failure means developers may be unaware their security assumptions are violated, increasing dwell time before detection.

Affected systems

LiquidJS versions 10.25.7 and earlier are affected. Version 10.26.0 and later include the fix. Any application embedding LiquidJS for template rendering—particularly those handling untrusted templates or using per-call ownPropertyOnly enforcement—should audit their deployment version and update accordingly. This includes Shopify app developers, static site generators using LiquidJS, and JavaScript backend services that render user-supplied Liquid templates.

Exploitability

Exploitation requires the ability to supply or influence Liquid template content that will be processed by a vulnerable LiquidJS instance. No authentication, interaction, or special network conditions are required. An attacker crafting a template with {% render %} tags can attempt prototype-chain traversal within the rendered partials. The attack is reliable and does not depend on user interaction or complex exploitation chains. However, successful information extraction depends on what sensitive properties exist in the application's prototype chain, which varies by use case.

Remediation

Upgrade LiquidJS to version 10.26.0 or later, which correctly propagates ownPropertyOnly settings through Context.spawn(). Developers should verify their deployment version immediately and prioritize patching in any environment processing untrusted templates. After upgrading, no configuration changes are necessary; the fix restores the intended behavior of per-call security overrides.

Patch guidance

Update the LiquidJS dependency to version 10.26.0 or higher. If using npm, run 'npm install liquidjs@^10.26.0'. Verify the upgrade by checking node_modules/liquidjs/package.json or running 'npm list liquidjs'. For applications in production, apply the patch in a staging environment first to confirm no template rendering behavior changes occur. The fix is backwards compatible and does not require updates to Liquid templates or application code.

Detection guidance

Identify instances of LiquidJS in use by examining package.json, package-lock.json, or yarn.lock files for liquidjs entries with version <= 10.25.7. In production, review any logs or monitoring for unusual prototype-chain access patterns, though this is difficult to detect without explicit instrumentation. Consider adding logging around parseAndRender() calls to track which security options are being set, and correlate that with actual rendering outcomes. If you have detailed template audit logs, look for {% render %} directives that appear after tightened security settings; these are the attack surface.

Why prioritize this

Although the CVSS score of 5.3 (Medium) reflects a confidentiality-only impact with low attack complexity, the vulnerability is particularly concerning because it represents a silent bypass of developer-intended security controls. Developers explicitly setting ownPropertyOnly: true have no indication their setting is ineffective within partials, creating a false sense of security. In multi-tenant or untrusted-template scenarios, this is a high-priority fix despite the medium rating. Patch quickly if you use LiquidJS with per-template security enforcement.

Risk score, explained

CVSS 5.3 reflects attack network accessibility (AV:N), low complexity (AC:L), no privilege requirement (PR:N), no user interaction (UI:N), and low confidentiality impact (C:L) with unchanged integrity and availability. The score correctly captures that this is an information disclosure via a network-accessible template engine, but it does not weight the significance of the security-control bypass or the likelihood of an attacker having template submission capability in real deployments. Organizations with high-sensitivity data in prototype chains or applications accepting untrusted templates should treat this as high-priority despite the medium score.

Frequently asked questions

Why is this a problem if ownPropertyOnly is an instance-level setting I can toggle globally?

The vulnerability exists because developers legitimately want to run a globally-permissive LiquidJS instance (ownPropertyOnly:false for backward compatibility) while still locking down specific, untrusted template renders via parseAndRender(..., { ownPropertyOnly: true }). The bug silently ignores that per-call lock inside {% render %} partials, defeating the intended isolation. This is a real-world use case in multi-tenant platforms and cannot be worked around without upgrading.

Can I work around this without upgrading?

No reliable workaround exists. You cannot verify whether ownPropertyOnly: true is actually enforced at runtime without code changes. Avoiding {% render %} tags in untrusted templates would mitigate the specific attack surface, but that's a fragile defense and incompatible with many legitimate Liquid templates. The only reliable fix is to upgrade to 10.26.0.

Does this vulnerability require the attacker to have administrative access or special permissions?

No. An attacker only needs the ability to supply Liquid template content that will be processed by the vulnerable LiquidJS instance. This could be via a template-upload feature, user-submitted content, a third-party plugin system, or any other path where untrusted input becomes a Liquid template. Authentication is not required.

How is this different from the previous ownPropertyOnly bypass discovered in array filters?

Both vulnerabilities stem from improper ownPropertyOnly handling in Context.spawn(), but they manifest through different Liquid features. The earlier CVE-2026-44399 covered array filters (where, reject, group_by, find, find_index, has). This CVE covers the {% render %} tag—a separately reachable sink that does not require filter usage. Both are now fixed in 10.26.0, but the dual exposure means prior patches for that CVE do not address this one.

This analysis is provided for informational purposes. The vulnerability details, versions, and remediation guidance are based on the published CVE and vendor advisory. Organizations should verify all version numbers and patch availability against official LiquidJS releases and GitHub advisories before deploying. Test patches in non-production environments first. If you believe you are impacted, consult your LiquidJS vendor and security team. Source: NVD (public-domain), retrieved 2026-07-27. Analysis generated by SEC.co (claude-haiku-4-5).