From a4f29d056b51b15e849701bed9dd0153acb777b4 Mon Sep 17 00:00:00 2001 From: Yang Jun Date: Sun, 14 Jun 2026 15:45:45 +0800 Subject: [PATCH] fix(security): charge sample filter full clone allocation to memoryLimit (CWE-770) Co-authored-by: Cursor --- src/filters/array.ts | 2 +- test/integration/liquid/dos.spec.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/filters/array.ts b/src/filters/array.ts index ed8776766..502af5355 100644 --- a/src/filters/array.ts +++ b/src/filters/array.ts @@ -254,7 +254,7 @@ export function sample (this: FilterImpl, v: T[] | string, count = 1): T | st v = toValue(v) if (isNil(v)) return [] if (!isArray(v)) v = stringify(v) - this.context.memoryLimit.use(count) + this.context.memoryLimit.use(v.length) const shuffled = [...v].sort(() => Math.random() - 0.5) if (count === 1) return shuffled[0] return shuffled.slice(0, count) diff --git a/test/integration/liquid/dos.spec.ts b/test/integration/liquid/dos.spec.ts index 1403c4d11..2aea12c48 100644 --- a/test/integration/liquid/dos.spec.ts +++ b/test/integration/liquid/dos.spec.ts @@ -84,6 +84,11 @@ describe('DoS related', function () { const liquid = new Liquid({ memoryLimit: 100 }) await expect(liquid.parseAndRender('{{ array | pop | size }}', { array })).rejects.toThrow('memory alloc limit exceeded') }) + it('should charge sample allocation to memoryLimit', async () => { + const array = Array(1e3).fill(0) + const liquid = new Liquid({ memoryLimit: 100 }) + await expect(liquid.parseAndRender('{{ array | sample: 1 | 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) }))