fix(filters): charge join/array_to_sentence_string by output size

join charged memoryLimit by array element count, not by the string it
produces, letting concat doubling (cheap reference copies) inflate an
array's element count and then materialize a huge string via join far
past the configured memoryLimit (GHSA-4r6h-5v86-94p3). Charge by the
sum of stringified element lengths plus separators before allocating.
Apply the same fix to the sibling array_to_sentence_string filter.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Yang Jun
2026-07-06 20:24:08 +08:00
co-authored by Cursor
parent 552819a84b
commit 4ef0aa3fe7
3 changed files with 38 additions and 4 deletions
+10 -3
View File
@@ -8,9 +8,16 @@ import { EmptyDrop } from '../drop'
export const join = argumentsToValue(function (this: FilterImpl, v: any[], arg: string) {
const array = toArray(v)
const sep = isNil(arg) ? ' ' : stringify(arg)
const complexity = array.length * (1 + sep.length)
this.context.memoryLimit.use(complexity)
return Array.prototype.join.call(array, sep)
const parts: string[] = []
let outputSize = array.length > 0 ? sep.length * (array.length - 1) : 0
for (let i = 0; i < array.length; i++) {
const item = array[i]
const part = isNil(item) ? '' : String(item)
outputSize += part.length
parts.push(part)
}
this.context.memoryLimit.use(outputSize)
return parts.join(sep)
})
export const last = argumentsToValue(function (this: FilterImpl, v: any) {
return isArrayLike(v) ? readArrayElement(v, -1, this.context.ownPropertyOnly) : ''
+3 -1
View File
@@ -209,7 +209,9 @@ export function number_of_words (this: FilterImpl, input: string, mode?: 'cjk' |
export function array_to_sentence_string (this: FilterImpl, array: unknown[], connector = 'and') {
connector = stringify(connector)
this.context.memoryLimit.use(array.length + connector.length)
let outputSize = connector.length + array.length * 2
for (let i = 0; i < array.length; i++) outputSize += stringify(array[i]).length
this.context.memoryLimit.use(outputSize)
switch (array.length) {
case 0:
return ''