Improve nested structure depth limit check

This commit is contained in:
jangko 2023-12-29 12:31:56 +07:00
parent c35151b035
commit ce214b000d
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
2 changed files with 35 additions and 23 deletions

View File

@ -70,6 +70,7 @@ type
line*: int line*: int
lineStartPos: int lineStartPos: int
tokenStart: int tokenStart: int
depthLimit: int
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private helpers # Private helpers
@ -110,6 +111,16 @@ template requireNextChar(lex: JsonLexer): char =
lex.checkForUnexpectedEof() lex.checkForUnexpectedEof()
lex.read() lex.read()
template enterNestedStructure(lex: JsonLexer, action: untyped) {.dirty.} =
inc lex.depthLimit
if lex.conf.nestedDepthLimit > 0 and
lex.depthLimit > lex.conf.nestedDepthLimit:
lex.err = errNestedDepthLimit
action
template exitNestedStructure(lex: JsonLexer) =
dec lex.depthLimit
proc handleLF(lex: var JsonLexer) = proc handleLF(lex: var JsonLexer) =
lex.advance lex.advance
lex.line += 1 lex.line += 1
@ -564,7 +575,7 @@ proc scanString*[T](lex: var JsonLexer, val: var T, limit: int)
else: else:
appendVal c appendVal c
proc scanValue*[T](lex: var JsonLexer, val: var T, depthLimit: int = 0) proc scanValue*[T](lex: var JsonLexer, val: var T)
{.gcsafe, raises: [IOError].} {.gcsafe, raises: [IOError].}
template parseObjectImpl*(lex: JsonLexer, template parseObjectImpl*(lex: JsonLexer,
@ -574,6 +585,8 @@ template parseObjectImpl*(lex: JsonLexer,
actionKey: untyped, actionKey: untyped,
actionValue: untyped, actionValue: untyped,
actionError: untyped) = actionError: untyped) =
lex.enterNestedStructure(actionError)
actionInitial actionInitial
lex.advance lex.advance
@ -635,7 +648,9 @@ template parseObjectImpl*(lex: JsonLexer,
else: else:
error(lex, errStringExpected, actionError) error(lex, errStringExpected, actionError)
proc scanObject*[T](lex: var JsonLexer, val: var T, depthLimit: int) lex.exitNestedStructure()
proc scanObject*[T](lex: var JsonLexer, val: var T)
{.gcsafe, raises: [IOError].} = {.gcsafe, raises: [IOError].} =
when T isnot (string or JsonVoid or JsonObjectType): when T isnot (string or JsonVoid or JsonObjectType):
{.fatal: "`scanObject` only accepts `string` or `JsonVoid` or `JsonObjectType`".} {.fatal: "`scanObject` only accepts `string` or `JsonVoid` or `JsonObjectType`".}
@ -667,12 +682,12 @@ proc scanObject*[T](lex: var JsonLexer, val: var T, depthLimit: int)
# value action # value action
when T is string: when T is string:
val.add ':' val.add ':'
lex.scanValue(val, depthLimit) lex.scanValue(val)
elif T is JsonVoid: elif T is JsonVoid:
lex.scanValue(val, depthLimit) lex.scanValue(val)
else: else:
var newVal: valueType(T) var newVal: valueType(T)
lex.scanValue(newVal, depthLimit) lex.scanValue(newVal)
if newVal.isNil.not: if newVal.isNil.not:
val[key] = newVal val[key] = newVal
do: do:
@ -687,6 +702,7 @@ template parseArrayImpl*(lex: JsonLexer,
actionValue: untyped, actionValue: untyped,
actionError: untyped) = actionError: untyped) =
lex.enterNestedStructure(actionError)
actionInitial actionInitial
lex.advance lex.advance
@ -741,7 +757,9 @@ template parseArrayImpl*(lex: JsonLexer,
if not lex.ok: actionError if not lex.ok: actionError
inc numElem inc numElem
proc scanArray*[T](lex: var JsonLexer, val: var T, depthLimit: int) lex.exitNestedStructure()
proc scanArray*[T](lex: var JsonLexer, val: var T)
{.gcsafe, raises: [IOError].} = {.gcsafe, raises: [IOError].} =
when T isnot (string or JsonVoid or seq[JsonValueRef]): when T isnot (string or JsonVoid or seq[JsonValueRef]):
{.fatal: "`scanArray` only accepts `string` or `JsonVoid` or `seq[JsonValueRef]`".} {.fatal: "`scanArray` only accepts `string` or `JsonVoid` or `seq[JsonValueRef]`".}
@ -761,23 +779,19 @@ proc scanArray*[T](lex: var JsonLexer, val: var T, depthLimit: int)
do: do:
# value action # value action
when T is (string or JsonVoid): when T is (string or JsonVoid):
lex.scanValue(val, depthLimit) lex.scanValue(val)
else: else:
val.setLen(numElem + 1) val.setLen(numElem + 1)
lex.scanValue(val[numElem], depthLimit) lex.scanValue(val[numElem])
do: do:
# error action # error action
return return
proc scanValue*[T](lex: var JsonLexer, val: var T, depthLimit: int) proc scanValue*[T](lex: var JsonLexer, val: var T)
{.gcsafe, raises: [IOError].} = {.gcsafe, raises: [IOError].} =
when T isnot (string or JsonVoid or JsonValueRef): when T isnot (string or JsonVoid or JsonValueRef):
{.fatal: "`scanValue` only accepts `string` or `JsonVoid` or `JsonValueRef`".} {.fatal: "`scanValue` only accepts `string` or `JsonVoid` or `JsonValueRef`".}
if lex.conf.nestedDepthLimit > 0 and
depthLimit > lex.conf.nestedDepthLimit:
error errNestedDepthLimit
var c = lex.nonws() var c = lex.nonws()
if not lex.ok: return if not lex.ok: return
@ -803,16 +817,16 @@ proc scanValue*[T](lex: var JsonLexer, val: var T, depthLimit: int)
of '{': of '{':
when T is JsonValueRef: when T is JsonValueRef:
val = T(kind: JsonValueKind.Object) val = T(kind: JsonValueKind.Object)
lex.scanObject(val.objVal, depthLimit+1) lex.scanObject(val.objVal)
else: else:
lex.scanObject(val, depthLimit+1) lex.scanObject(val)
if not lex.ok: return if not lex.ok: return
of '[': of '[':
when T is JsonValueRef: when T is JsonValueRef:
val = T(kind: JsonValueKind.Array) val = T(kind: JsonValueKind.Array)
lex.scanArray(val.arrayVal, depthLimit+1) lex.scanArray(val.arrayVal)
else: else:
lex.scanArray(val, depthLimit+1) lex.scanArray(val)
if not lex.ok: return if not lex.ok: return
of 't', 'f': of 't', 'f':
when T is JsonVoid: when T is JsonVoid:

View File

@ -115,7 +115,7 @@ template testScanValue(input: string, output: untyped,
value == output value == output
lex.err == error lex.err == error
suite "numbers test suite": suite "lexer test suite":
test "scanInt string": test "scanInt string":
testScanInt("1234567890", "1234567890") testScanInt("1234567890", "1234567890")
testScanInt("01234567890", "0", error = errLeadingZero) testScanInt("01234567890", "0", error = errLeadingZero)
@ -587,8 +587,8 @@ suite "numbers test suite":
testScanValue("[[[1]]]", "[[[1]]]") testScanValue("[[[1]]]", "[[[1]]]")
conf.nestedDepthLimit = 3 conf.nestedDepthLimit = 3
testScanValue("[[[[1]]]]", "[[[[", error = errNestedDepthLimit, conf = conf) testScanValue("[[[[1]]]]", "[[[", error = errNestedDepthLimit, conf = conf)
testScanValue("[ { \"a\": [ { \"b\": 3}] } ]", "[{\"a\":[{\"b\":", testScanValue("[ { \"a\": [ { \"b\": 3}] } ]", "[{\"a\":[",
error = errNestedDepthLimit, conf = conf) error = errNestedDepthLimit, conf = conf)
testScanValue("{ \"a\": 1234.567 // comments\n }", testScanValue("{ \"a\": 1234.567 // comments\n }",
@ -781,9 +781,7 @@ suite "numbers test suite":
arrayVal: @[ arrayVal: @[
JsonValueRef[uint64](kind: JsonValueKind.Array, JsonValueRef[uint64](kind: JsonValueKind.Array,
arrayVal: @[ arrayVal: @[
JsonValueRef[uint64](kind: JsonValueKind.Array, arrayVal: @[ JsonValueRef[uint64](kind: JsonValueKind.Array)
JsonValueRef[uint64](nil)
])
]) ])
]) ])
]), error = errNestedDepthLimit, conf = conf) ]), error = errNestedDepthLimit, conf = conf)