fix(deps): update dependency liquidjs to v10.25.5 [security]#666
Open
renovate[bot] wants to merge 1 commit intomasterfrom
Open
fix(deps): update dependency liquidjs to v10.25.5 [security]#666renovate[bot] wants to merge 1 commit intomasterfrom
renovate[bot] wants to merge 1 commit intomasterfrom
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
10.25.2→10.25.5GitHub Vulnerability Alerts
CVE-2026-34166
Summary
The
replacefilter in LiquidJS incorrectly accounts for memory usage when thememoryLimitoption is enabled. It chargesstr.length + pattern.length + replacement.lengthbytes to the memory limiter, but the actual output fromstr.split(pattern).join(replacement)can be quadratically larger when the pattern occurs many times in the input string. This allows an attacker who controls template content to bypass thememoryLimitDoS protection with approximately 2,500x amplification, potentially causing out-of-memory conditions.Details
The vulnerable code is in
src/filters/string.ts:137-142:The
memoryLimit.use()call charges only the sum of the three input lengths. However, thestr.split(pattern).join(replacement)operation produces output of size:When every character in
strmatchespattern(e.g.,str= 5,000as,pattern=a), there are 5,000 occurrences. With a 5,000-character replacement string, the output is5000 * 5000 = 25,000,000characters, while only5000 + 1 + 5000 = 10,001bytes are charged to the limiter.The
Limiterclass atsrc/util/limiter.ts:3-22is a simple accumulator — it only checks at the timeuse()is called and has no post-hoc validation of actual memory allocated.The
memoryLimitoption defaults toInfinity(src/liquid-options.ts:198), so this only affects deployments that explicitly enable memory limiting to protect against untrusted template input.PoC
Impact
Users who deploy LiquidJS with
memoryLimitenabled to process untrusted templates (e.g., multi-tenant SaaS platforms allowing custom templates) are not protected against memory exhaustion via thereplacefilter. An attacker who can author templates can allocate ~2,500x more memory than the configured limit allows, potentially causing:The impact is limited to availability (no confidentiality or integrity impact), and requires both non-default configuration (
memoryLimitenabled) and template authoring access.Recommended Fix
Account for the actual output size in the memory limiter by calculating the number of occurrences:
This computes the exact output size: the original string length plus, for each occurrence, the difference between the replacement and pattern lengths. The
split()result is reused to avoid computing it twice.CVE-2026-35525
Summary
LiquidJS enforces partial and layout root restrictions using the resolved pathname string, but it does not resolve the canonical filesystem path before opening the file. A symlink placed inside an allowed partials or layouts directory can therefore point to a file outside that directory and still be loaded.
Details
For
{% include %},{% render %}, and{% layout %}, LiquidJS checks whether the candidate path is inside the configured partials or layouts roots before reading it. That check is path-based, not realpath-based.Because of that, a file like
partials/link.liquidpasses the directory containment check as long as its pathname is under the allowed root. Iflink.liquidis actually a symlink to a file outside the allowed root, the filesystem follows the symlink when the file is opened and LiquidJS renders the external target.So the restriction is applied to the path string that was requested, not to the file that is actually read.
This matters in environments where an attacker can place templates or otherwise influence files under a trusted template root, including uploaded themes, extracted archives, mounted content, or repository-controlled template trees.
PoC
Impact
If an attacker can place or influence symlinks under a trusted partials or layouts directory, they can make LiquidJS read and render files outside the intended template root. In practice this can expose arbitrary readable files reachable through symlink targets.
CVE-2026-39412
Summary
The
sort_naturalfilter bypasses theownPropertyOnlysecurity option, allowing template authors to extract values of prototype-inherited properties through a sorting side-channel attack. Applications relying onownPropertyOnly: trueas a security boundary (e.g., multi-tenant template systems) are exposed to information disclosure of sensitive prototype properties such as API keys and tokens.Details
In
src/filters/array.ts, thesort_naturalfunction (lines 40-48) accesses object properties using direct bracket notation (lhs[propertyString]), which traverses the JavaScript prototype chain:In contrast, the correct approach used elsewhere in the codebase goes through
readJSPropertyinsrc/context/context.ts, which checkshasOwnPropertywhenownPropertyOnlyis enabled:The
sort_naturalfilter bypasses this check entirely. Thesortfilter (lines 26-38 in the same file) has the same issue.PoC
Result:
The sorted order reveals that the target's prototype
apiKeyfalls between "aaa" and "zzz". By using more precise probe values, the full secret can be extracted character-by-character through binary search.Impact
Information disclosure vulnerability. Any application using LiquidJS with
ownPropertyOnly: true(the default since v10.x) where untrusted users can write templates is affected. Attackers can extract prototype-inherited secrets (API keys, tokens, passwords) from context objects via thesort_naturalorsortfilters, bypassing the security control that is supposed to prevent prototype property access.CVE-2026-39859
liquidjs10.25.0 documentsrootas constraining filenames passed torenderFile()andparseFile(), but top-level file loads do not enforce that boundary.The published npm package
liquidjs@10.25.0on Linux 6.17.0 with Node v22.22.1. ALiquidinstance configured with an empty temporary directory asrootstill returned the contents of/etc/hostswhenrenderFile('/etc/hosts')was called. I have not exhaustively checked older releases yet; 10.25.0 is the latest tested version.Root cause:
src/parser/parser.ts:83-85callsloader.lookup(file, LookupType.Root, ...)and then reads the returned file.src/fs/loader.ts:38passestype !== LookupType.Rootintocandidates().LookupType.Root,enforceRootis false, sosrc/fs/loader.ts:47-66accepts resolved absolute paths and fallback results without anycontains()check.This appears adjacent to the March 10, 2026 fix for CVE-2026-30952, which hardened
include/render/layoutbut not the top-level file-loading APIs.Proof of concept:
Expected result: a path outside
rootshould be rejected.Actual result:
/etc/hostsis rendered successfully.Impact: any application that treats
rootas a sandbox boundary and forwards attacker-controlled template names intorenderFile()orparseFile()can disclose arbitrary local files readable by the server process.Suggested fix: apply the same containment checks used for partial/layout lookups to
LookupType.Root, and reject absolute or fallback paths unless they remain within an allowed root. A regression test should verify thatrenderFile('/etc/hosts')fails whenrootpoints to an unrelated directory.Release Notes
harttle/liquidjs (liquidjs)
v10.25.5Compare Source
Bug Fixes
v10.25.4Compare Source
Bug Fixes
v10.25.3Compare Source
Bug Fixes
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.