mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-16 12:50:38 -07:00
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]>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: 安全模型
|
||||
---
|
||||
|
||||
LiquidJS 提供了面向 DoS 的限制选项(`parseLimit`、`renderLimit`、`memoryLimit`)来降低风险。本文按统一结构说明每个限制的作用范围,以及你在生产环境应采用的安全边界。
|
||||
|
||||
## 安全边界
|
||||
|
||||
内置限制是协作式防护,不是严格的运行时隔离。
|
||||
|
||||
- 它**不等于**进程的 RSS/heap 实际占用。
|
||||
- 它**不是** JavaScript 沙箱。
|
||||
- 在生产环境中应结合进程/容器资源限制和请求超时做分层防护。
|
||||
|
||||
## 限制速览
|
||||
|
||||
- [parseLimit][parseLimit]:限制每次 `parse()` 的模板总长度。
|
||||
- [renderLimit][renderLimit]:限制每次 `render()` 的总渲染时间。
|
||||
- [memoryLimit][memoryLimit]:协作式限制 LiquidJS 已记账的内存敏感分配。
|
||||
|
||||
## 限制详解
|
||||
|
||||
### parseLimit
|
||||
|
||||
[parseLimit][parseLimit] 限制每次 `.parse()` 调用中解析的模板大小(字符长度),包括引用的 partials 和 layouts。由于 LiquidJS 解析模板字符串的时间复杂度接近 O(n),限制模板总长度通常就足够了。
|
||||
|
||||
普通电脑可以很容易处理 `1e8`(100M)个字符的模板。
|
||||
|
||||
### renderLimit
|
||||
|
||||
仅限制模板大小是不够的,因为在渲染时可能会出现动态的数组和循环。[renderLimit][renderLimit] 通过限制每次 `render()` 调用的时间来缓解这些问题。
|
||||
|
||||
```liquid
|
||||
{%- for i in (1..10000000) -%}
|
||||
order: {{i}}
|
||||
{%- endfor -%}
|
||||
```
|
||||
|
||||
渲染时间是在渲染每个模板之前检查的。在上面的例子中,循环中有两个模板:`order: ` 和 `{{i}}`,因此会检查 2x10000000 次。
|
||||
|
||||
`renderLimit` 不是硬性的 CPU 限制器。它是在模板渲染边界做检查,因此检查点之间的高计算开销过滤器/标签/用户自定义函数,或深层模板嵌套,仍可能导致 DoS。
|
||||
|
||||
### memoryLimit
|
||||
|
||||
`memoryLimit` 只限制 LiquidJS 显式记账到的操作。
|
||||
|
||||
- 会被统计:LiquidJS 内部调用了内存记账逻辑的内存敏感操作。
|
||||
- 不保证被统计:任意用户对象行为(例如自定义 `toValue()` / `toString()` 链)以及其他发生在 LiquidJS 记账点之外的宿主侧分配。
|
||||
|
||||
换句话说,`memoryLimit` 限制的是 LiquidJS 的“已记账分配”,而不是进程里每一个字节的分配。
|
||||
|
||||
即使模板和迭代次数较少,内存使用量也可能呈指数增长。在下面的示例中,内存会在每次迭代中翻倍:
|
||||
|
||||
```liquid
|
||||
{% assign array = "1,2,3" | split: "," %}
|
||||
{% for i in (1..32) %}
|
||||
{% assign array = array | concat: array %}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
由于 [JavaScript 使用 GC 来管理内存](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_management),`memoryLimit` 可能无法反映实际的内存占用。
|
||||
|
||||
## 在线服务建议
|
||||
|
||||
如果你运行在线服务,建议尽量避免渲染完全由用户定义的模板。
|
||||
|
||||
- 优先使用受控模板或受限模板子集。
|
||||
- 如果必须支持用户自定义模板,请隔离渲染(worker/进程/容器),并同时配置操作系统或容器级的内存/CPU 限额与请求限流。
|
||||
- 将 `parseLimit` / `renderLimit` / `memoryLimit` 视为 DoS 防护体系中的一层,而不是唯一防线。
|
||||
|
||||
对于单个模板中的重型操作,仍建议使用进程级隔离(例如 [paralleljs][paralleljs])。
|
||||
|
||||
[paralleljs]: https://www.npmjs.com/package/paralleljs
|
||||
[parseLimit]: /api/interfaces/LiquidOptions.html#parseLimit
|
||||
[renderLimit]: /api/interfaces/LiquidOptions.html#renderLimit
|
||||
[memoryLimit]: /api/interfaces/LiquidOptions.html#memoryLimit
|
||||
Reference in New Issue
Block a user