From 8734b5fc6e4b20ade86518336a072468d0be8785 Mon Sep 17 00:00:00 2001 From: Yang Jun Date: Sun, 14 Jun 2026 00:14:00 +0800 Subject: [PATCH] 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 --- src/filters/array.ts | 6 ++++-- test/integration/liquid/dos.spec.ts | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/filters/array.ts b/src/filters/array.ts index f22fcce20..ed8776766 100644 --- a/src/filters/array.ts +++ b/src/filters/array.ts @@ -88,8 +88,10 @@ export function unshift (this: FilterImpl, v: T[], arg: T): T[] { return clone } -export function pop (v: T[]): T[] { - const clone = [...toArray(v)] +export function pop (this: FilterImpl, v: T[]): T[] { + const array = toArray(v) + this.context.memoryLimit.use(array.length) + const clone = [...array] clone.pop() return clone } diff --git a/test/integration/liquid/dos.spec.ts b/test/integration/liquid/dos.spec.ts index 85783564e..1403c4d11 100644 --- a/test/integration/liquid/dos.spec.ts +++ b/test/integration/liquid/dos.spec.ts @@ -79,6 +79,11 @@ describe('DoS related', function () { await expect(liquid.parseAndRender(src, { array, count: 3 })).resolves.toBe('a a a a a a a a') await expect(liquid.parseAndRender(src, { array, count: 100 })).rejects.toThrow('memory alloc limit exceeded, line:1, col:26') }) + it('should charge pop allocation to memoryLimit', async () => { + const array = Array(1e3).fill(0) + const liquid = new Liquid({ memoryLimit: 100 }) + await expect(liquid.parseAndRender('{{ array | pop | size }}', { array })).rejects.toThrow('memory alloc limit exceeded') + }) it('should charge strip_html input length to memoryLimit', () => { const liquid = new Liquid({ memoryLimit: 100 }) expect(() => liquid.parseAndRenderSync('{{ s | strip_html }}', { s: 'a'.repeat(200) }))