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
lineStartPos: int
tokenStart: int
depthLimit: int
# ------------------------------------------------------------------------------
# Private helpers
@ -110,6 +111,16 @@ template requireNextChar(lex: JsonLexer): char =
lex.checkForUnexpectedEof()
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) =
lex.advance
lex.line += 1
@ -564,7 +575,7 @@ proc scanString*[T](lex: var JsonLexer, val: var T, limit: int)
else:
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].}
template parseObjectImpl*(lex: JsonLexer,
@ -574,6 +585,8 @@ template parseObjectImpl*(lex: JsonLexer,
actionKey: untyped,
actionValue: untyped,
actionError: untyped) =
lex.enterNestedStructure(actionError)
actionInitial
lex.advance
@ -635,7 +648,9 @@ template parseObjectImpl*(lex: JsonLexer,
else:
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].} =
when T isnot (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
when T is string:
val.add ':'
lex.scanValue(val, depthLimit)
lex.scanValue(val)
elif T is JsonVoid:
lex.scanValue(val, depthLimit)
lex.scanValue(val)
else:
var newVal: valueType(T)
lex.scanValue(newVal, depthLimit)
lex.scanValue(newVal)
if newVal.isNil.not:
val[key] = newVal
do:
@ -687,6 +702,7 @@ template parseArrayImpl*(lex: JsonLexer,
actionValue: untyped,
actionError: untyped) =
lex.enterNestedStructure(actionError)
actionInitial
lex.advance
@ -741,7 +757,9 @@ template parseArrayImpl*(lex: JsonLexer,
if not lex.ok: actionError
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].} =
when T isnot (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:
# value action
when T is (string or JsonVoid):
lex.scanValue(val, depthLimit)
lex.scanValue(val)
else:
val.setLen(numElem + 1)
lex.scanValue(val[numElem], depthLimit)
lex.scanValue(val[numElem])
do:
# error action
return
proc scanValue*[T](lex: var JsonLexer, val: var T, depthLimit: int)
proc scanValue*[T](lex: var JsonLexer, val: var T)
{.gcsafe, raises: [IOError].} =
when T isnot (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()
if not lex.ok: return
@ -803,16 +817,16 @@ proc scanValue*[T](lex: var JsonLexer, val: var T, depthLimit: int)
of '{':
when T is JsonValueRef:
val = T(kind: JsonValueKind.Object)
lex.scanObject(val.objVal, depthLimit+1)
lex.scanObject(val.objVal)
else:
lex.scanObject(val, depthLimit+1)
lex.scanObject(val)
if not lex.ok: return
of '[':
when T is JsonValueRef:
val = T(kind: JsonValueKind.Array)
lex.scanArray(val.arrayVal, depthLimit+1)
lex.scanArray(val.arrayVal)
else:
lex.scanArray(val, depthLimit+1)
lex.scanArray(val)
if not lex.ok: return
of 't', 'f':
when T is JsonVoid:

View File

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