Skip to content

Commit 44a7e73

Browse files
authored
Merge pull request #418 from bbrks/configurable_maxDepth
Add MaxDepth as a config option
2 parents dc11f49 + 2834c7e commit 44a7e73

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

api_tests/config_test.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package test
22

33
import (
44
"encoding/json"
5+
"fmt"
6+
"strings"
57
"testing"
68

7-
"github.com/json-iterator/go"
9+
jsoniter "github.com/json-iterator/go"
810
"github.com/stretchr/testify/require"
911
)
1012

@@ -24,6 +26,44 @@ func Test_customize_float_marshal(t *testing.T) {
2426
should.Equal("1.234568", str)
2527
}
2628

29+
func Test_max_depth(t *testing.T) {
30+
deepJSON := func(depth int) []byte {
31+
return []byte(strings.Repeat(`[`, depth) + strings.Repeat(`]`, depth))
32+
}
33+
34+
tests := []struct {
35+
jsonDepth int
36+
cfgMaxDepth int
37+
expectedErr string
38+
}{
39+
// Test the default depth
40+
{jsonDepth: 10000, cfgMaxDepth: 0},
41+
{jsonDepth: 10001, cfgMaxDepth: 0, expectedErr: "max depth"},
42+
// Test max depth logic
43+
{jsonDepth: 5, cfgMaxDepth: 6},
44+
{jsonDepth: 5, cfgMaxDepth: 5},
45+
{jsonDepth: 5, cfgMaxDepth: 4, expectedErr: "max depth"},
46+
// Try a large depth without a limit
47+
{jsonDepth: 128000, cfgMaxDepth: -1},
48+
}
49+
50+
for _, test := range tests {
51+
t.Run(fmt.Sprintf("jsonDepth:%v_cfgMaxDepth:%v", test.jsonDepth, test.cfgMaxDepth), func(t *testing.T) {
52+
should := require.New(t)
53+
cfg := jsoniter.Config{MaxDepth: test.cfgMaxDepth}.Froze()
54+
55+
var val interface{}
56+
err := cfg.Unmarshal(deepJSON(test.jsonDepth), &val)
57+
if test.expectedErr != "" {
58+
should.Error(err)
59+
should.Contains(err.Error(), test.expectedErr)
60+
} else {
61+
should.NoError(err)
62+
}
63+
})
64+
}
65+
}
66+
2767
func Test_customize_tag_key(t *testing.T) {
2868

2969
type TestObject struct {

config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import (
1111
"github.com/modern-go/reflect2"
1212
)
1313

14+
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
15+
const defaultMaxDepth = 10000
16+
1417
// Config customize how the API should behave.
1518
// The API is created from Config by Froze.
1619
type Config struct {
@@ -25,6 +28,7 @@ type Config struct {
2528
ValidateJsonRawMessage bool
2629
ObjectFieldMustBeSimpleString bool
2730
CaseSensitive bool
31+
MaxDepth int
2832
}
2933

3034
// API the public interface of this package.
@@ -56,6 +60,7 @@ var ConfigCompatibleWithStandardLibrary = Config{
5660
EscapeHTML: true,
5761
SortMapKeys: true,
5862
ValidateJsonRawMessage: true,
63+
MaxDepth: -1, // encoding/json has no max depth (stack overflow at 2581101)
5964
}.Froze()
6065

6166
// ConfigFastest marshals float with only 6 digits precision
@@ -80,6 +85,7 @@ type frozenConfig struct {
8085
streamPool *sync.Pool
8186
iteratorPool *sync.Pool
8287
caseSensitive bool
88+
maxDepth int
8389
}
8490

8591
func (cfg *frozenConfig) initCache() {
@@ -127,13 +133,17 @@ func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
127133

128134
// Froze forge API from config
129135
func (cfg Config) Froze() API {
136+
if cfg.MaxDepth == 0 {
137+
cfg.MaxDepth = defaultMaxDepth
138+
}
130139
api := &frozenConfig{
131140
sortMapKeys: cfg.SortMapKeys,
132141
indentionStep: cfg.IndentionStep,
133142
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
134143
onlyTaggedField: cfg.OnlyTaggedField,
135144
disallowUnknownFields: cfg.DisallowUnknownFields,
136145
caseSensitive: cfg.CaseSensitive,
146+
maxDepth: cfg.MaxDepth,
137147
}
138148
api.streamPool = &sync.Pool{
139149
New: func() interface{} {

iter.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,9 @@ func (iter *Iterator) Read() interface{} {
327327
}
328328
}
329329

330-
// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
331-
const maxDepth = 10000
332-
333330
func (iter *Iterator) incrementDepth() (success bool) {
334331
iter.depth++
335-
if iter.depth <= maxDepth {
332+
if iter.depth <= iter.cfg.maxDepth || iter.cfg.maxDepth < 0 {
336333
return true
337334
}
338335
iter.ReportError("incrementDepth", "exceeded max depth")

0 commit comments

Comments
 (0)