chore: more strict mem limit for string filters

This commit is contained in:
Harttle
2026-03-22 22:10:39 +08:00
parent 95ddefc056
commit 94440a0653
3 changed files with 47 additions and 30 deletions
+44 -23
View File
@@ -11,7 +11,7 @@
// Hiragana (Japanese): \u3040-\u309F
// Hangul (Korean): \uAC00-\uD7AF
import { FilterImpl } from '../template'
import { assert, escapeRegExp, stringify } from '../util'
import { assert, stringify } from '../util'
const rCJKWord = /[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/gu
@@ -38,10 +38,14 @@ export function lstrip (this: FilterImpl, v: string, chars?: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
if (chars) {
chars = escapeRegExp(stringify(chars))
return str.replace(new RegExp(`^[${chars}]+`, 'g'), '')
chars = stringify(chars)
this.context.memoryLimit.use(chars.length)
for (let i = 0, set = new Set(chars); i < str.length; i++) {
if (!set.has(str[i])) return str.slice(i)
}
return ''
}
return str.replace(/^\s+/, '')
return str.trimStart()
}
export function downcase (this: FilterImpl, v: string) {
@@ -58,20 +62,22 @@ export function upcase (this: FilterImpl, v: string) {
export function remove (this: FilterImpl, v: string, arg: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
return str.split(stringify(arg)).join('')
arg = stringify(arg)
this.context.memoryLimit.use(str.length + arg.length)
return str.split(arg).join('')
}
export function remove_first (this: FilterImpl, v: string, l: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
return str.replace(stringify(l), '')
l = stringify(l)
this.context.memoryLimit.use(str.length + l.length)
return str.replace(l, '')
}
export function remove_last (this: FilterImpl, v: string, l: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
const pattern = stringify(l)
this.context.memoryLimit.use(str.length + pattern.length)
const index = str.lastIndexOf(pattern)
if (index === -1) return str
return str.substring(0, index) + str.substring(index + pattern.length)
@@ -81,10 +87,14 @@ export function rstrip (this: FilterImpl, str: string, chars?: string) {
str = stringify(str)
this.context.memoryLimit.use(str.length)
if (chars) {
chars = escapeRegExp(stringify(chars))
return str.replace(new RegExp(`[${chars}]+$`, 'g'), '')
chars = stringify(chars)
this.context.memoryLimit.use(chars.length)
for (let i = str.length - 1, set = new Set(chars); i >= 0; i--) {
if (!set.has(str[i])) return str.slice(0, i + 1)
}
return ''
}
return str.replace(/\s+$/, '')
return str.trimEnd()
}
export function split (this: FilterImpl, v: string, arg: string) {
@@ -101,10 +111,13 @@ export function strip (this: FilterImpl, v: string, chars?: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
if (chars) {
chars = escapeRegExp(stringify(chars))
return str
.replace(new RegExp(`^[${chars}]+`, 'g'), '')
.replace(new RegExp(`[${chars}]+$`, 'g'), '')
const set = new Set(stringify(chars))
this.context.memoryLimit.use(set.size)
let i = 0
let j = str.length - 1
while (set.has(str[i])) i++
while (j >= i && set.has(str[j])) j--
return str.slice(i, j + 1)
}
return str.trim()
}
@@ -123,36 +136,43 @@ export function capitalize (this: FilterImpl, str: string) {
export function replace (this: FilterImpl, v: string, pattern: string, replacement: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
return str.split(stringify(pattern)).join(replacement)
pattern = stringify(pattern)
this.context.memoryLimit.use(str.length + pattern.length + replacement.length)
return str.split(pattern).join(replacement)
}
export function replace_first (this: FilterImpl, v: string, arg1: string, arg2: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
return str.replace(stringify(arg1), arg2)
arg1 = stringify(arg1)
arg2 = stringify(arg2)
this.context.memoryLimit.use(str.length + arg1.length + arg2.length)
return str.replace(arg1, arg2)
}
export function replace_last (this: FilterImpl, v: string, arg1: string, arg2: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
const pattern = stringify(arg1)
this.context.memoryLimit.use(pattern.length)
const index = str.lastIndexOf(pattern)
if (index === -1) return str
const replacement = stringify(arg2)
this.context.memoryLimit.use(replacement.length)
return str.substring(0, index) + replacement + str.substring(index + pattern.length)
}
export function truncate (this: FilterImpl, v: string, l = 50, o = '...') {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
o = stringify(o)
this.context.memoryLimit.use(str.length + o.length)
if (str.length <= l) return v
return str.substring(0, l - o.length) + o
}
export function truncatewords (this: FilterImpl, v: string, words = 15, o = '...') {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
o = stringify(o)
this.context.memoryLimit.use(str.length + o.length)
const arr = str.split(/\s+/)
if (words <= 0) words = 1
let ret = arr.slice(0, words).join(' ')
@@ -187,7 +207,8 @@ export function number_of_words (this: FilterImpl, input: string, mode?: 'cjk' |
}
export function array_to_sentence_string (this: FilterImpl, array: unknown[], connector = 'and') {
this.context.memoryLimit.use(array.length)
connector = stringify(connector)
this.context.memoryLimit.use(array.length + connector.length)
switch (array.length) {
case 0:
return ''
+3 -3
View File
@@ -1,5 +1,5 @@
import { FS } from './fs'
import { assert, escapeRegex } from '../util'
import { assert } from '../util'
export interface LoaderOptions {
fs: FS;
@@ -24,8 +24,8 @@ export class Loader {
if (options.relativeReference) {
const sep = options.fs.sep
assert(sep, '`fs.sep` is required for relative reference')
const rRelativePath = new RegExp(['.' + sep, '..' + sep, './', '../'].map(prefix => escapeRegex(prefix)).join('|'))
this.shouldLoadRelative = (referencedFile: string) => rRelativePath.test(referencedFile)
const prefixes = ['.' + sep, '..' + sep, './', '../']
this.shouldLoadRelative = (referencedFile: string) => prefixes.some(prefix => referencedFile.startsWith(prefix))
} else {
this.shouldLoadRelative = (_referencedFile: string) => false
}
-4
View File
@@ -22,10 +22,6 @@ export function isIterator (val: any): val is IterableIterator<any> {
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return)
}
export function escapeRegex (str: string) {
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
}
export function promisify<T1, T2> (fn: (arg1: T1, cb: (err: Error | null, result: T2) => void) => void): (arg1: T1) => Promise<T2>;
export function promisify<T1, T2, T3> (fn: (arg1: T1, arg2: T2, cb: (err: Error | null, result: T3) => void) => void): (arg1: T1, arg2: T2) => Promise<T3>;
export function promisify (fn: any) {