Commit Graph
1304 Commits
Author SHA1 Message Date
Yang JunandCursor 5359301864 docs: replace CookieHub with cookieconsent
Co-authored-by: Cursor <[email protected]>
2026-06-22 01:30:23 +08:00
4775227358 docs(security): route vulnerability reports to GitHub Advisories (#913)
Replace the private email contact with GitHub Security Advisories and
set the common-case fix expectation to within a month.

Co-authored-by: Cursor <[email protected]>
2026-06-20 00:00:05 +08:00
8a0c74a7fc fix(security): charge pop filter allocation to memoryLimit (#907)
* fix(security): charge pop filter allocation to memoryLimit (CWE-770)

The `pop` array filter cloned the input via `[...toArray(v)]` without
charging `this.context.memoryLimit.use(...)`, bypassing the memoryLimit
DoS guard that its sibling filters (shift, unshift, compact, etc.) apply.
Mirror `shift` to account for the O(N) allocation.

Co-authored-by: Cursor <[email protected]>

* fix(security): charge sample filter full clone allocation to memoryLimit (CWE-770)

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-06-14 15:50:50 +08:00
ed15a52c26 docs: revisit wording & style for liquidjs.com (#906)
* docs: polish theme, playground, and reference pages

Improve readability of the docs site with updated light/dark tokens, shared
code-block styling, and playground editors that follow system color scheme.
Skip CookieHub on localhost, serve the browser bundle from theme source, and
use backtick titles on filter/tag reference pages for consistent navigation.

Co-authored-by: Cursor <[email protected]>

* docs: highlight npx in bash blocks and polish English copy

Use Prism insertBefore for CLI commands like npx, tighten tutorial and reference wording, and keep YAML titles free of backticks so sidebar and page headings stay correct.

Co-authored-by: Cursor <[email protected]>

* docs: restore lowercase filter and tag titles

Titles should match actual filter/tag identifiers (e.g. abs, append), not capitalized English labels.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-06-08 00:26:52 +08:00
499b221f33 docs: remove translations & update homepage (#904)
* docs: add GitHub buttons and improve option docs

* chore: replace husky with prepush check

* docs: revamp homepage and switch to custom GitHub buttons

- Make the docs English-only by removing all zh-cn content, the language switcher UI, and related JS/config

- Rework homepage feature cards (Safe & Typed, Pure JavaScript, Shopify & Jekyll, Streaming) and refresh section colors/layout

- Replace buttons.github.io with custom Star/Sponsor buttons featuring a live star count and dark-mode support

- Drop the buttons.js script and tidy banner, header, footer, and share partials

Co-authored-by: Cursor <[email protected]>

* fix: restore tsconfig settings and changelog build

Re-add suppressImplicitAnyIndexErrors and downlevelIteration removed in
a75033e2c, which broke the rollup TypeScript build on CI. Drop zh-cn
changelog output now that translations were removed.

Co-authored-by: Cursor <[email protected]>

* fix: resolve TS errors without deprecated tsconfig options

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-06-07 01:37:51 +08:00
Yang JunandGitHub 705e5d1b6e docs: add consent banner and GA (#903) 2026-06-06 14:04:21 +08:00
semantic-release-bot a8fd734b5e chore(release): 10.27.0 [skip ci]
# [10.27.0](https://github.com/harttle/liquidjs/compare/v10.26.0...v10.27.0) (2026-05-15)

### Features

* **context:** null-prototype scope frames via createScope ([#899](https://github.com/harttle/liquidjs/issues/899)) ([47d3f1b](https://github.com/harttle/liquidjs/commit/47d3f1b1cf33be91fe587821f288d1c9d8e1ace7))
v10.27.0
2026-05-15 18:21:41 +00:00
47d3f1b1cf feat(context): null-prototype scope frames via createScope (#899)
- Add createScope() building Object.create(null) with optional own props

- Initialize context stack bottom with createScope() for assign/capture

- Push null-proto scopes from for, tablerow, block, layout, include (incl. Jekyll)

Co-authored-by: Cursor <[email protected]>
2026-05-16 02:20:03 +08:00
semantic-release-bot c20c0af02d chore(release): 10.26.0 [skip ci]
# [10.26.0](https://github.com/harttle/liquidjs/compare/v10.25.7...v10.26.0) (2026-05-14)

### Bug Fixes

* **date:** cap strftime widths and account padding in memoryLimit ([#895](https://github.com/harttle/liquidjs/issues/895)) ([3129d46](https://github.com/harttle/liquidjs/commit/3129d46dc95efa357b00e5a57ee1af80a13d72ed))
* enforce renderLimit for empty renderTemplates calls ([#894](https://github.com/harttle/liquidjs/issues/894)) ([5b9c346](https://github.com/harttle/liquidjs/commit/5b9c3469085e01c79e2d0af28e2a13f730e1793d))
* propagate ownPropertyOnly into Context.spawn() for {% render %} ([#893](https://github.com/harttle/liquidjs/issues/893)) ([dbbf628](https://github.com/harttle/liquidjs/commit/dbbf6288030591bf6da28d8c1cce5a17bca97bb6))
* **security:** block Object.prototype filter/tag lookups (RCE) ([#897](https://github.com/harttle/liquidjs/issues/897)) ([457fae0](https://github.com/harttle/liquidjs/commit/457fae0736c3ec862539b9dbf7f477e6c08fb6c6))
* strip html newline tags ([#892](https://github.com/harttle/liquidjs/issues/892)) ([26ea285](https://github.com/harttle/liquidjs/commit/26ea2856c7a90aec892b98d94a9b7a3e18539045))
* **strip_html:** rewrite as linear single-pass scan to avoid ReDoS ([#896](https://github.com/harttle/liquidjs/issues/896)) ([3616a74](https://github.com/harttle/liquidjs/commit/3616a744b9abeb425c217b340a2397d46176afb8))

### Features

* add sha256 and hmac_sha256 filters for cryptographic operations ([#889](https://github.com/harttle/liquidjs/issues/889)) ([1c816d4](https://github.com/harttle/liquidjs/commit/1c816d4fc3bcd2cba011f7a84f56a4251fca0622))
v10.26.0
2026-05-14 14:23:44 +00:00
457fae0736 fix(security): block Object.prototype filter/tag lookups (RCE) (#897)
* fix(security): block Object.prototype filter/tag lookups (RCE)

`liquid.filters` and `liquid.tags` were plain `{}` so bracket access on
template-controlled keys inherited from `Object.prototype`. Most damaging:
`{{ x | valueOf }}` resolved to `Object.prototype.valueOf`, which the
filter pipeline called as a handler with `this = FilterImpl`; valueOf
returns its receiver, leaking `context`, `liquid`, `token` (and via them
parser, loader, fs) into the template — chain that with `group_by`/`where`
gadgets and an attacker reaches `Function`/`child_process` for RCE.
Same shape on the tag side: `{% constructor %}` bypassed the
"tag not found" assertion and crashed with a confusing message.

Use null-prototype storage so `liquid.filters[name]` / `liquid.tags[name]`
only resolve to explicitly registered entries. The existing
`assert(impl || !strictFilters)` and `assert(TagClass, ...)` now do the
right thing for `valueOf`, `toString`, `constructor`, `__proto__`,
`hasOwnProperty`, `isPrototypeOf`, `__defineGetter__`, etc.

Co-authored-by: Cursor <[email protected]>

* test: fold prototype-registry regressions into register + e2e

Co-authored-by: Cursor <[email protected]>

* test: assert null-prototype registries vs all Object.prototype keys

Co-authored-by: Cursor <[email protected]>

* test: dedupe registry checks; merge filter prototype loop

Co-authored-by: Cursor <[email protected]>

* fix(context): use null-prototype scope and register objects

Add createScope(); use for bottom scope, spawn default, getAll merge, ctx.push frames, filter loops, include/layout blocks registers, and cycle groups. registers uses Object.create(null) and getRegister uses ??.

For-loop continue register defaults to 0 (not {}): Array.slice coerces plain {} but not null-prototype objects.

Export createScope from the package entry.

Co-authored-by: Cursor <[email protected]>

* revert(context): plain {} registers and getRegister ||

Registers are only mutated by tag implementations, not templates; keep null-prototype scopes/createScope for push frames.

Co-authored-by: Cursor <[email protected]>

* test(context): assert scope isolation without probing prototypes

Replace Object.getPrototypeOf checks for bottom() and getAll() with
'in' checks on typical Object.prototype names plus a merge assertion.

Co-authored-by: Cursor <[email protected]>

* test(e2e): assert constructor filter/tag lookups (node + UMD)

Co-authored-by: Cursor <[email protected]>

* test(context): cover Object.prototype keys under ownPropertyOnly

- Add getSync cases for constructor and valueOf on plain objects
- Remove scope storage tests that used the in operator

Co-authored-by: Cursor <[email protected]>

* refactor: remove createScope helper

Drop the exported helper and finish migrating call sites. Revert incidental context/for/include/layout churn so behavior matches mainline aside from the removal. Trim duplicate e2e and heavy Object.prototype loops in registry tests.

Co-authored-by: Cursor <[email protected]>

* docs: document ownPropertyOnly and Drop security in security model

Co-authored-by: Cursor <[email protected]>

* docs(zh-cn): sync security model with ownPropertyOnly and Drop notes

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-05-14 22:18:10 +08:00
3616a744b9 fix(strip_html): rewrite as linear single-pass scan to avoid ReDoS (#896)
* fix(strip_html): rewrite as linear single-pass scan to avoid ReDoS

The previous strip_html regex
  /<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<[\s\S]*?>|<!--[\s\S]*?-->/g
contains lazy alternatives that backtrack O(n^2) on inputs with many
unclosed `<script` / `<style` openers. A 350KB payload of
`'<script'.repeat(50000)` blocked the Node.js event loop for ~10s, and
cost grew quadratically with input size. memoryLimit only charged
str.length, which does not bound regex CPU.

Replace the regex with an indexOf-based single-pass scan. For each `<`
we:
- if `<script` opener: find next `</script>` and skip the whole block;
  cache "no closer after pos k" so subsequent unclosed `<script`
  openers do not re-scan the tail.
- same for `<style` / `</style>`.
- otherwise treat as a generic `<...>` tag (matches the original
  behavior, where the `<[\s\S]*?>` alternative also caught comments).
- if no closing `>` exists, emit the tail as literal text and stop.

Total work is O(n). All existing strip_html test cases pass unchanged.

Add regression tests covering the PoCs (`<script` / `<style` repeats,
and `<script>foo` repeats with `>` but no `</script>`) plus a
memoryLimit assertion.

Co-authored-by: Cursor <[email protected]>

* refactor(strip_html): factor block kinds into a small table

Same algorithm and complexity, fewer lines. Document why a regex-only
solution can't be O(n) in V8 (no atomic groups / possessive quantifiers
/ memoization, so unrolled-loop patterns are still O(n^2) on unclosed
openers — empirically confirmed: original 280KB ~4s, Friedl unrolled
~14s, atomic lookahead ~7s; tokenizer ~1ms).

Co-authored-by: Cursor <[email protected]>

* refactor(strip_html): inline block kinds to match file style

Drop the module-level STRIP_BLOCKS table; the rest of the file keeps
each filter self-contained (only escapeMap/unescapeMap are top-level
maps shared across filters). Two openers don't justify a table.

Co-authored-by: Cursor <[email protected]>

* refactor(strip_html): unify raw-text blocks; treat <!--...--> as opaque

In HTML5, <script>, <style>, and <!-- --> are all raw-text blocks: their
content is opaque until the matching closer, so a `>` inside CSS, JS, or
a comment must not be treated as a tag end. The previous code only had
this special handling for <script> and <style>; comments containing `>`
fell through to the generic `<...>` branch and were partially stripped
(e.g. `<!-- a > b -->` left `b -->` in the output).

Match Shopify Liquid's STRIP_HTML_BLOCKS set (script + style + comment),
and consolidate the three near-identical branches into a small
opener/closer table inside the function.

Algorithm and complexity unchanged (O(n) via indexOf + cached closer
positions). Add a regression test for `>` inside a comment.

Co-authored-by: Cursor <[email protected]>

* refactor(strip_html): drop position cache, delete dead blocks from Set

Once `indexOf(closer, X)` returns -1, all subsequent searches (with
monotonically increasing start) also return -1. So tracking absence is
enough; storing positions is unnecessary. Make `blocks` a Set and
delete a kind once its closer is known absent — no parallel `dead`
bookkeeping. Use Jest's per-test timeout for the ReDoS regressions
instead of manual Date.now() bookkeeping.

Co-authored-by: Cursor <[email protected]>

* refactor(strip_html): treat '<...>' as a catch-all block kind

Adding ['<', '>'] as the lowest-priority entry of `blocks` lets the
inner loop subsume the generic-tag fallback: the `end` sentinel and
its `< 0` / `<= 0` follow-up checks disappear, the "no terminator"
exit becomes a single `i === lt` test, and Set<[string, string]>
collapses to Map<string, string>.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-05-11 23:59:40 +08:00
3129d46dc9 fix(date): cap strftime widths and account padding in memoryLimit (#895)
* fix(date): cap strftime widths and account padding in memoryLimit

- Clamp numeric strftime pad widths to MAX_STRFTIME_PAD (1024)
- Export estimateStrftimePaddingMemory for the date filter to charge memoryLimit
- Replace unbounded pad() concatenation loop with ch.repeat + single concat
- Add regression tests for clamping and memoryLimit on huge %width directives

Co-authored-by: Cursor <[email protected]>

* fix(date): harden strftime memory accounting and document security model

Move strftime memory charging into the same formatting path used for padding, enforce pre-allocation checks, and add regression tests for non-string date format PoCs. Add dedicated docs clarifying that memoryLimit is cooperative DoS mitigation and not strict heap isolation.

Co-authored-by: Cursor <[email protected]>

* docs(zh-cn): add security model docs for DoS limits

Add a Chinese security-model tutorial and link it from the Chinese DoS guide to clarify that memoryLimit is cooperative accounting, list uncounted custom conversion cases, and recommend avoiding fully user-defined templates in online services.

Co-authored-by: Cursor <[email protected]>

* docs: consolidate DoS docs into security-model pages

Merge DoS guidance into security-model docs in both English and Chinese, and remove the placeholder dos.md pages to avoid duplicate/redirect-only docs.

Co-authored-by: Cursor <[email protected]>

* docs: merge DoS details into security-model docs

Move the detailed parseLimit/renderLimit/memoryLimit explanations and examples into the English and Chinese security-model pages so content from the removed dos pages is preserved.

Co-authored-by: Cursor <[email protected]>

* docs: reorganize security-model structure for clarity

Restructure English and Chinese security-model docs into a consistent flow: security boundary, limits overview, per-limit details, and online service guidance.

Co-authored-by: Cursor <[email protected]>

* refactor(strftime): simplify %N width parsing logic

Use regex-backed width assumptions to simplify %N width normalization and padding memory accounting while keeping behavior equivalent.

Co-authored-by: Cursor <[email protected]>

* refactor(strftime): rely on memoryLimit for width control

Remove MAX_STRFTIME_PAD hard capping and rely on memoryLimit enforcement before padding allocation. Update strftime/date tests and security-model docs to match the new boundary and renderLimit caveats.

Co-authored-by: Cursor <[email protected]>

* fix(strftime): use add() once for padding, minimize churn

- pad(): replace per-char loop with a single add(str, ch.repeat(n)) call.
  The earlier `probe[0] === ch` heuristic was wrong when ch happened to
  equal a leading char of 'probe' (e.g. ch === 'p').
- strftime.ts: revert unrelated typing/structural refactors so the diff
  contains only the memoryLimit threading and the %N memory charge.
- docs: rewire the deleted dos.html sidebar entry to security-model.html
  (with localized labels) so the deleted page does not 404 from the
  sidebar.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-05-10 14:35:28 +08:00
5b9c346908 fix: enforce renderLimit for empty renderTemplates calls (#894)
renderLimit was only checked inside the per-template loop, so
renderTemplates([], ...) skipped it. Empty {% for %} and {% tablerow %}
bodies call that path once per iteration (tablerow still does emitter
work for <tr>/<td>), bypassing the documented time budget. Check the
limiter at renderTemplates entry before the loop.

Add regression tests for empty for-body and empty tablerow-body.

Co-authored-by: Cursor <[email protected]>
2026-05-07 23:03:43 +08:00
dbbf628803 fix: propagate ownPropertyOnly into Context.spawn() for {% render %} (#893)
Child contexts from spawn() re-derived ownPropertyOnly from Liquid opts
only, dropping per-render RenderOptions overrides. That broke the contract
that parseAndRender(..., { ownPropertyOnly: true }) locks down a single
render, including partials loaded via {% render %}.

Add regression test matching prototype-chain leak PoC.

Co-authored-by: Cursor <[email protected]>
2026-05-03 22:35:31 +08:00
26ea2856c7 fix: strip html newline tags (#892)
* docs: add @talboren as financial contributor

* fix(strip_html): match tags that span newlines inside angle brackets

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-05-03 21:36:09 +08:00
a55f543f49 docs(readme): add Freshet to Who's Using LiquidJS (#888)
Co-authored-by: MattAltermatt <[email protected]>
2026-05-03 12:05:12 +08:00
allcontributors[bot]GitHuballcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
d1d517d1ec docs: add VladimirFilonov as a contributor for code (#891)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2026-05-03 12:03:52 +08:00
Vladimir FilonovandGitHub 1c816d4fc3 feat: add sha256 and hmac_sha256 filters for cryptographic operations (#889) 2026-05-03 12:03:26 +08:00
semantic-release-bot 34877950bf chore(release): 10.25.7 [skip ci]
## [10.25.7](https://github.com/harttle/liquidjs/compare/v10.25.6...v10.25.7) (2026-04-23)

### Bug Fixes

* **filters:** support Buffer input in base64_encode to prevent binary data corruption ([#881](https://github.com/harttle/liquidjs/issues/881)) ([0ee6dbb](https://github.com/harttle/liquidjs/commit/0ee6dbb511aa926f6d490293282060abf3bab37f))
v10.25.7
2026-04-23 13:41:11 +00:00
Yang JunandGitHub 75c815a4d7 docs: add @talboren as financial contributor (#886) 2026-04-23 21:39:50 +08:00
allcontributors[bot]GitHuballcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
f1f896c29d docs: add talboren as a contributor for code (#885)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2026-04-23 21:31:50 +08:00
TalandGitHub 0ee6dbb511 fix(filters): support Buffer input in base64_encode to prevent binary data corruption (#881)
* fix: support Buffer input in base64_encode filter

When binary data (e.g. images, PDFs) is passed through the template
context as a Node.js Buffer, the base64_encode filter would call
stringify() on it first, which internally does String(value). This
triggers Buffer.toString() with the default 'utf-8' encoding, which
is a lossy conversion for non-UTF-8 byte sequences — invalid bytes
get replaced with U+FFFD, permanently destroying the original data.

The fix checks for Buffer.isBuffer() before stringify, and calls
buffer.toString('base64') directly, bypassing the lossy UTF-8
intermediate step. String inputs continue through the existing path
unchanged.

Made-with: Cursor

* fix: handle Buffer in filter layer to fix browser build

Move Buffer handling from base64-impl.ts (which gets swapped for the
browser impl at build time) into base64.ts (the filter layer). This
avoids a type error during the browser rollup build where the browser
impl only accepts string.

Also guard Buffer.isBuffer() with typeof Buffer !== 'undefined' for
safety in browser environments.

Made-with: Cursor
2026-04-23 21:30:58 +08:00
semantic-release-bot 30e04ba16d chore(release): 10.25.6 [skip ci]
## [10.25.6](https://github.com/harttle/liquidjs/compare/v10.25.5...v10.25.6) (2026-04-19)

### Bug Fixes

* nested block for layout ([#883](https://github.com/harttle/liquidjs/issues/883)) ([e2311df](https://github.com/harttle/liquidjs/commit/e2311dfd6e82f73509308aa8a3a1fafc92e226f0))
v10.25.6
2026-04-19 15:42:53 +00:00
Yang JunandGitHub e2311dfd6e fix: nested block for layout (#883) 2026-04-19 23:41:35 +08:00
Yang JunandGitHub 2def22c85e docs(readme): add Kibana to README.md (#882)
* docs(readme): add Kibana and Sentry to Who's Using LiquidJS

Made-with: Cursor

* docs(readme): drop Semgrep and Sentry from Who's Using; keep Kibana

Made-with: Cursor

* docs(readme): restore Sentry in financial sponsors block

Made-with: Cursor

* docs(readme): restore Timmy Braun in all-contributors table

Made-with: Cursor
2026-04-19 21:38:15 +08:00
semantic-release-bot 4af7be695c chore(release): 10.25.5 [skip ci]
## [10.25.5](https://github.com/harttle/liquidjs/compare/v10.25.4...v10.25.5) (2026-04-07)

### Bug Fixes

* enforce root containment for renderFile/parseFile lookups ([#870](https://github.com/harttle/liquidjs/issues/870)) ([f41c1fc](https://github.com/harttle/liquidjs/commit/f41c1fc02fe901598f3328118b42b13bc6bc9b04))
* null date should return empty ([#868](https://github.com/harttle/liquidjs/issues/868)) ([#872](https://github.com/harttle/liquidjs/issues/872)) ([4f9a499](https://github.com/harttle/liquidjs/commit/4f9a49988a93c156524981e189a4fec238e682b8))
* rounding negative away from zero when half ([#873](https://github.com/harttle/liquidjs/issues/873)) ([1cdf10b](https://github.com/harttle/liquidjs/commit/1cdf10b57d82f0592414efbfca19e204b37aea9f))
v10.25.5
2026-04-07 17:18:16 +00:00
Yang JunandGitHub 05c47da46d refactor: replace shell scripts with JS for cross-platform support (#875)
Convert bin/ shell scripts to Node.js and npm scripts using shx and npm-run-all2. Remove unused build-icons.sh. Inlined simple scripts (build-docs-liquid, build-apidoc) as npm scripts.

Made-with: Cursor
2026-04-08 01:16:49 +08:00
allcontributors[bot]GitHuballcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
66011d14b0 docs: add timbze as a contributor for code (#874)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2026-04-08 00:53:27 +08:00
Timmy BraunandGitHub 1cdf10b57d fix: rounding negative away from zero when half (#873) 2026-04-08 00:52:35 +08:00
Timmy BraunandGitHub 4f9a49988a fix: null date should return empty (#868) (#872) 2026-04-08 00:10:33 +08:00
Yang JunandGitHub f41c1fc02f fix: enforce root containment for renderFile/parseFile lookups (#870)
Made-with: Cursor
2026-04-07 23:18:53 +08:00
semantic-release-bot db4348507e chore(release): 10.25.4 [skip ci]
## [10.25.4](https://github.com/harttle/liquidjs/compare/v10.25.3...v10.25.4) (2026-04-07)

### Bug Fixes

* sort and sort_natural filters bypass ownPropertyOnly ([#869](https://github.com/harttle/liquidjs/issues/869)) ([e743da0](https://github.com/harttle/liquidjs/commit/e743da0020d34e2ee547e1cc1a86b58377ebe1ce))
v10.25.4
2026-04-07 13:02:49 +00:00
Yang JunandGitHub e743da0020 fix: sort and sort_natural filters bypass ownPropertyOnly (#869)
Use _getFromScope for property access in sort/sort_natural filters to respect the ownPropertyOnly security option, preventing prototype chain traversal that could leak sensitive inherited properties.

Also extract shared sortBy helper, add orderedCompare with nil handling consistent with caseInsensitiveCompare and Ruby Liquid.

Made-with: Cursor
2026-04-07 21:01:20 +08:00
semantic-release-bot 8f69a08399 chore(release): 10.25.3 [skip ci]
## [10.25.3](https://github.com/harttle/liquidjs/compare/v10.25.2...v10.25.3) (2026-04-06)

### Bug Fixes

* precise memoryLimit for string replace ([abc058b](https://github.com/harttle/liquidjs/commit/abc058be0f33d6372cd2216f4945183167abeb25))
* use realpath for fs.contains ([#867](https://github.com/harttle/liquidjs/issues/867)) ([529dd67](https://github.com/harttle/liquidjs/commit/529dd67eeb6b125637623d6a723601f0938d3613))
v10.25.3
2026-04-06 06:45:50 +00:00
Yang JunandGitHub 529dd67eeb fix: use realpath for fs.contains (#867)
* fix: use realpath for fs.contains

* chore: reset file mode changes

Made-with: Cursor

* fix: Windows compat for contains/containsSync and toLiquidAsync arg order

Made-with: Cursor
2026-04-06 14:40:35 +08:00
Harttle abc058be0f fix: precise memoryLimit for string replace 2026-03-26 19:36:31 +08:00
semantic-release-bot 521177e3f6 chore(release): 10.25.2 [skip ci]
## [10.25.2](https://github.com/harttle/liquidjs/compare/v10.25.1...v10.25.2) (2026-03-25)

### Bug Fixes

* handle undefined replacement argument in replace filter ([#864](https://github.com/harttle/liquidjs/issues/864)) ([0ad2b11](https://github.com/harttle/liquidjs/commit/0ad2b11ab15e7da608a9ef936b2a00a6a6517038))
v10.25.2
2026-03-25 17:20:39 +00:00
allcontributors[bot]GitHuballcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
75e06eff92 docs: add joecottam as a contributor for code (#865)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2026-03-26 01:19:19 +08:00
Joe CottamandGitHub 0ad2b11ab1 fix: handle undefined replacement argument in replace filter (#864) 2026-03-26 01:18:40 +08:00
semantic-release-bot 97d829116c chore(release): 10.25.1 [skip ci]
## [10.25.1](https://github.com/harttle/liquidjs/compare/v10.25.0...v10.25.1) (2026-03-22)

### Bug Fixes

* mem limiter for invalid ranges ([95ddefc](https://github.com/harttle/liquidjs/commit/95ddefc056a11a44d9e753fd47a39db2c241e578))
* treat args for replace_first as literal ([35d5230](https://github.com/harttle/liquidjs/commit/35d523026345d80458df24c72e653db78b5d061d))
v10.25.1
2026-03-22 14:18:52 +00:00
Harttle 35d5230263 fix: treat args for replace_first as literal 2026-03-22 22:16:45 +08:00
Harttle 94440a0653 chore: more strict mem limit for string filters 2026-03-22 22:10:39 +08:00
Yang JunandHarttle 95ddefc056 fix: mem limiter for invalid ranges 2026-03-22 10:24:03 +08:00
Yang JunandGitHub 1b85fdaa9c docs: update contact in security.md (#862) 2026-03-08 15:41:45 +08:00
semantic-release-bot 93c38c7c6d chore(release): 10.25.0 [skip ci]
# [10.25.0](https://github.com/harttle/liquidjs/compare/v10.24.0...v10.25.0) (2026-03-07)

### Bug Fixes

* path traversal vulnerability, [#851](https://github.com/harttle/liquidjs/issues/851) ([#855](https://github.com/harttle/liquidjs/issues/855)) ([3cd024d](https://github.com/harttle/liquidjs/commit/3cd024d652dc883c46307581e979fe32302adbac))

### Features

* export error types, resolving [#837](https://github.com/harttle/liquidjs/issues/837) ([#840](https://github.com/harttle/liquidjs/issues/840)) ([71aa1b1](https://github.com/harttle/liquidjs/commit/71aa1b1998a3a66e536af67c6ea8947a28616eaf))
v10.25.0
2026-03-07 20:01:42 +00:00
Yang JunandGitHub c7a291b46b chore: update semantic-release dependencies (#861) 2026-03-08 04:00:26 +08:00
Yang JunandGitHub eb4683ee3f chore: update to NPM Trusted Release (#860) 2026-03-08 03:35:49 +08:00
Yang JunandGitHub f1fc573a65 docs: state differences regarding inspect array/hash, #852, #853 (#858) 2026-03-08 03:14:13 +08:00
allcontributors[bot]GitHuballcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
524cd92cfe docs: add peaktwilight as a contributor for code (#857)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2026-03-08 02:40:16 +08:00
allcontributors[bot]GitHuballcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
0d9e797889 docs: add MorielHarush as a contributor for code (#856)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2026-03-08 02:39:45 +08:00