• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

harttle / liquidjs / 25865220001
100%

Build:
DEFAULT BRANCH: master
Ran 14 May 2026 02:19PM UTC
Jobs 1
Files 118
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

14 May 2026 02:18PM UTC coverage: 99.542%. Remained the same
25865220001

push

github

web-flow
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 <cursoragent@cursor.com>

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

Co-authored-by: Cursor <cursoragent@cursor.com>

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

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: dedupe registry checks; merge filter prototype loop

Co-authored-by: Cursor <cursoragent@cursor.com>

* 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 <cursoragent@cursor.com>

* revert(... (continued)

1128 of 1140 branches covered (98.95%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

3001 of 3008 relevant lines covered (99.77%)

22136.21 hits per line

Jobs
ID Job ID Ran Files Coverage
1 25865220001.1 14 May 2026 02:19PM UTC 118
99.54
GitHub Action Run
Source Files on build 25865220001
  • Tree
  • List 118
  • Changed 0
  • Source Changed 0
  • Coverage Changed 0
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • 457fae07 on github
  • Prev Build on master (#25681518010)
  • Next Build on master (#25934164122)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc