mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
457fae0736c3ec862539b9dbf7f477e6c08fb6c6
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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]>
|
||
|
|
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]> |