Files
liquidjs/docs/source/tutorials/security-model.md
T
Yang JunandCursor 1e8ce453a5 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]>
2026-05-09 22:06:06 +08:00

3.8 KiB

title
title
Security Model

LiquidJS provides DoS-oriented limits (parseLimit, renderLimit, memoryLimit) to reduce risk, but these limits are cooperative safeguards, not strict runtime isolation.

memoryLimit is cooperative

memoryLimit tracks memory-sensitive allocations inside LiquidJS code paths that explicitly account for them. It is best-effort mitigation for template-driven abuse, not a strict heap cap.

  • It does not equal process RSS/heap usage.
  • It does not sandbox JavaScript execution.
  • It should be combined with process/container limits and request timeouts for production defense-in-depth.

What it limits (and what it does not)

memoryLimit only limits operations that LiquidJS itself counts.

  • Counted: memory-sensitive LiquidJS operations that call internal memory accounting.
  • Not guaranteed counted: arbitrary user object behavior such as custom toValue()/toString() chains, or other host-side code that allocates outside LiquidJS accounting points.

In other words, memoryLimit limits what LiquidJS counts, not every byte your process may allocate.

Guidance for online services

If you run an online service, avoid rendering fully user-defined templates whenever possible.

  • Prefer curated templates or a restricted template subset.
  • If user-defined templates are required, isolate rendering (worker/process/container), enforce OS/container memory and CPU limits, and apply request rate limits.
  • Treat parseLimit/renderLimit/memoryLimit as one layer in a broader DoS defense strategy.

DoS limits quick reference

LiquidJS provides 3 DoS-oriented options:

  • parseLimit: limit total template size per parse() call.
  • renderLimit: limit total render time per render() call.
  • memoryLimit: cooperatively limit memory-sensitive allocations counted by LiquidJS.

For heavy single-template operations, process-level isolation is still recommended (for example with paralleljs).

DoS limits details

parseLimit

parseLimit restricts the size (character length) of templates parsed in each .parse() call, including referenced partials and layouts. Since LiquidJS parses template strings in near O(n) time, limiting total template length is usually sufficient.

A typical PC handles 1e8 (100M) characters without issues.

renderLimit

Restricting template size alone is insufficient because dynamic loops with large counts can occur in render time. renderLimit mitigates this by limiting the time consumed by each render() call.

{%- for i in (1..10000000) -%}
    order: {{i}}
{%- endfor -%}

Render time is checked on a per-template basis (before rendering each template). In the above example, there are 2 templates in the loop: order: and {{i}}, render time will be checked 10000000x2 times.

For time-consuming tags and filters within a single template, the process can still hang.

memoryLimit

Even with small number of templates and iterations, memory usage can grow exponentially. In the following example, memory doubles with each iteration:

{% assign array = "1,2,3" | split: "," %}
{% for i in (1..32) %}
    {% assign array = array | concat: array %}
{% endfor %}

memoryLimit restricts memory-sensitive filters to prevent excessive memory allocation. As JavaScript uses GC to manage memory, memoryLimit limits only the total number of objects allocated by memory-sensitive operations in LiquidJS and may not reflect the actual memory footprint.