From ce214b000df15da6bf0c4242f87568524efda6d6 Mon Sep 17 00:00:00 2001 From: jangko Date: Fri, 29 Dec 2023 12:31:56 +0700 Subject: [PATCH] Improve nested structure depth limit check --- json_serialization/lexer.nim | 48 +++++++++++++++++++++++------------- tests/test_lexer.nim | 10 +++----- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/json_serialization/lexer.nim b/json_serialization/lexer.nim index 6cd7fa9..042a7e2 100644 --- a/json_serialization/lexer.nim +++ b/json_serialization/lexer.nim @@ -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: diff --git a/tests/test_lexer.nim b/tests/test_lexer.nim index 3beb8cd..3a5a181 100644 --- a/tests/test_lexer.nim +++ b/tests/test_lexer.nim @@ -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)