mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-19 06:20:38 -07:00
feat: remove memoryLimit; add templateLimit, outputLengthLimit, maxDepth (#937)
* feat: remove memoryLimit option (#910) Co-authored-by: Cursor <[email protected]> * feat: add templateLimit, outputLengthLimit, and maxDepth DoS limits Enforce v11 resource guards in render and tags, fix for offset/else behavior, and update tutorials for Tag-class registration. Co-authored-by: Cursor <[email protected]> * docs: revert unnecessary tutorial churn from memoryLimit PR Restore the two-example register-filters-tags structure (Value + Hash) and undo unrelated constructor/emitter doc edits not required for DoS limits. Co-authored-by: Cursor <[email protected]> * docs: trim security-model prose and update render-tag-content Remove diary-style engine comparisons from security-model.md. Update render-tag-content tutorial to Tag class examples with tpls class field. Co-authored-by: Cursor <[email protected]> * docs: note maxDepth stack overflow applies to renderSync only Explain why async render does not need maxDepth for stack protection based on generator/toPromise driving. Co-authored-by: Cursor <[email protected]> * refactor: track maxDepth via depthLimit Limiter on Context Replace increaseDepth/decreaseDepth with a shared Limiter that supports paired use/release, matching templateLimit and outputLengthLimit patterns. Co-authored-by: Cursor <[email protected]> * fix: remove spurious diff noise in filter files Restore misc.ts from origin/next with LF line endings and re-apply only memoryLimit removal, avoiding CRLF and blank-line churn in the export block. Co-authored-by: Cursor <[email protected]> * refactor: minimize PR diff noise Co-authored-by: Cursor <[email protected]> * feat: cap strftime pad width at 1M docs: restructure security model with production guidance Co-authored-by: Cursor <[email protected]> * refactor: simplify depthLimit in partial tags and tighten security docs Drop try/finally around depthLimit in include, layout, and render; release at generator end. Consolidate production guidance in security-model.md. Fix padded-blocks lint in dos.spec.ts. Co-authored-by: Cursor <[email protected]> --------- Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+14
-8
@@ -1,13 +1,15 @@
|
||||
import { changeCase, padStart, padEnd } from './underscore'
|
||||
import { LiquidDate } from './liquid-date'
|
||||
import type { Limiter } from './limiter'
|
||||
import { assert } from './assert'
|
||||
|
||||
/** Per-conversion numeric width cap for strftime (%N, %15d, …). */
|
||||
export const MAX_STRFTIME_PAD = 1024 * 1024
|
||||
|
||||
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/
|
||||
interface FormatOptions {
|
||||
flags: Record<string, boolean>;
|
||||
width?: string;
|
||||
modifier?: string;
|
||||
memoryLimit?: Pick<Limiter, 'use'>;
|
||||
}
|
||||
|
||||
// prototype extensions
|
||||
@@ -98,8 +100,8 @@ const formatCodes: Record<string, FormatCodeHandler> = {
|
||||
M: (d: LiquidDate) => d.getMinutes(),
|
||||
N: (d: LiquidDate, opts: FormatOptions) => {
|
||||
const width = Number(opts.width) || 9
|
||||
assertPadWidth(width)
|
||||
const str = String(d.getMilliseconds()).slice(0, width)
|
||||
opts.memoryLimit?.use(width - str.length)
|
||||
return padEnd(str, width, '0')
|
||||
},
|
||||
p: (d: LiquidDate) => (d.getHours() < 12 ? 'AM' : 'PM'),
|
||||
@@ -123,25 +125,29 @@ const formatCodes: Record<string, FormatCodeHandler> = {
|
||||
}
|
||||
formatCodes.h = formatCodes.b
|
||||
|
||||
export function strftime (d: LiquidDate, formatStr: string, memoryLimit?: Pick<Limiter, 'use'>) {
|
||||
export function strftime (d: LiquidDate, formatStr: string) {
|
||||
let output = ''
|
||||
let remaining = formatStr
|
||||
let match
|
||||
while ((match = rFormat.exec(remaining))) {
|
||||
output += remaining.slice(0, match.index)
|
||||
remaining = remaining.slice(match.index + match[0].length)
|
||||
output += format(d, match, memoryLimit)
|
||||
output += format(d, match)
|
||||
}
|
||||
return output + remaining
|
||||
}
|
||||
|
||||
function format (d: LiquidDate, match: RegExpExecArray, memoryLimit?: Pick<Limiter, 'use'>) {
|
||||
function assertPadWidth (width: number) {
|
||||
assert(width <= MAX_STRFTIME_PAD, 'strftime pad width limit exceeded')
|
||||
}
|
||||
|
||||
function format (d: LiquidDate, match: RegExpExecArray) {
|
||||
const [input, flagStr = '', width, modifier, conversion] = match
|
||||
const convert = formatCodes[conversion]
|
||||
if (!convert) return input
|
||||
const flags: Record<string, boolean> = {}
|
||||
for (const flag of flagStr) flags[flag] = true
|
||||
let ret = String(convert(d, { flags, width, modifier, memoryLimit }))
|
||||
let ret = String(convert(d, { flags, width, modifier }))
|
||||
let padChar = padSpaceChars.has(conversion) ? ' ' : '0'
|
||||
let padWidth = Number(width) || padWidths[conversion] || 0
|
||||
if (flags['^']) ret = ret.toUpperCase()
|
||||
@@ -149,6 +155,6 @@ function format (d: LiquidDate, match: RegExpExecArray, memoryLimit?: Pick<Limit
|
||||
if (flags['_']) padChar = ' '
|
||||
else if (flags['0']) padChar = '0'
|
||||
if (flags['-']) padWidth = 0
|
||||
memoryLimit?.use(Number(padWidth) - ret.length)
|
||||
else assertPadWidth(padWidth)
|
||||
return padStart(ret, padWidth, padChar)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user