diff --git a/src/filters/misc.ts b/src/filters/misc.ts index 57ed57812..264bbb172 100644 --- a/src/filters/misc.ts +++ b/src/filters/misc.ts @@ -9,13 +9,15 @@ function defaultFilter (this: FilterImpl, value: T1, def return isFalsy(value, this.context) ? defaultValue : value } -function json (value: any, space = 0) { - return JSON.stringify(value, null, space) +function json (this: FilterImpl, value: any, space = 0) { + const output = JSON.stringify(value, null, space) + this.context.memoryLimit.use(output.length) + return output } -function inspect (value: any, space = 0) { +function inspect (this: FilterImpl, value: any, space = 0) { const ancestors: object[] = [] - return JSON.stringify(value, function (this: unknown, _key: unknown, value: any) { + const output = JSON.stringify(value, function (this: unknown, _key: unknown, value: any) { if (typeof value !== 'object' || value === null) return value // `this` is the object that value is contained in, i.e., its direct parent. while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) ancestors.pop() @@ -23,6 +25,8 @@ function inspect (value: any, space = 0) { ancestors.push(value) return value }, space) + this.context.memoryLimit.use(output.length) + return output } function to_integer (value: any) { diff --git a/test/integration/liquid/dos.spec.ts b/test/integration/liquid/dos.spec.ts index 2aea12c48..277b37f38 100644 --- a/test/integration/liquid/dos.spec.ts +++ b/test/integration/liquid/dos.spec.ts @@ -89,6 +89,21 @@ describe('DoS related', function () { const liquid = new Liquid({ memoryLimit: 100 }) await expect(liquid.parseAndRender('{{ array | sample: 1 | size }}', { array })).rejects.toThrow('memory alloc limit exceeded') }) + it('should charge json allocation to memoryLimit', async () => { + const data = Array(1e3).fill({ k: 'value' }) + const liquid = new Liquid({ memoryLimit: 100 }) + await expect(liquid.parseAndRender('{{ data | json }}', { data })).rejects.toThrow('memory alloc limit exceeded') + }) + it('should charge jsonify allocation to memoryLimit', async () => { + const data = Array(1e3).fill({ k: 'value' }) + const liquid = new Liquid({ memoryLimit: 100 }) + await expect(liquid.parseAndRender('{{ data | jsonify }}', { data })).rejects.toThrow('memory alloc limit exceeded') + }) + it('should charge inspect allocation to memoryLimit', async () => { + const data = Array(1e3).fill({ k: 'value' }) + const liquid = new Liquid({ memoryLimit: 100 }) + await expect(liquid.parseAndRender('{{ data | inspect }}', { data })).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) }))