Kaynağa Gözat

Merge pull request #418 from bbrks/configurable_maxDepth

Add MaxDepth as a config option
Tao Wen 6 yıl önce
ebeveyn
işleme
44a7e7340d
3 değiştirilmiş dosya ile 52 ekleme ve 5 silme
  1. 41 1
      api_tests/config_test.go
  2. 10 0
      config.go
  3. 1 4
      iter.go

+ 41 - 1
api_tests/config_test.go

@@ -2,9 +2,11 @@ package test
 
 import (
 	"encoding/json"
+	"fmt"
+	"strings"
 	"testing"
 
-	"github.com/json-iterator/go"
+	jsoniter "github.com/json-iterator/go"
 	"github.com/stretchr/testify/require"
 )
 
@@ -24,6 +26,44 @@ func Test_customize_float_marshal(t *testing.T) {
 	should.Equal("1.234568", str)
 }
 
+func Test_max_depth(t *testing.T) {
+	deepJSON := func(depth int) []byte {
+		return []byte(strings.Repeat(`[`, depth) + strings.Repeat(`]`, depth))
+	}
+
+	tests := []struct {
+		jsonDepth   int
+		cfgMaxDepth int
+		expectedErr string
+	}{
+		// Test the default depth
+		{jsonDepth: 10000, cfgMaxDepth: 0},
+		{jsonDepth: 10001, cfgMaxDepth: 0, expectedErr: "max depth"},
+		// Test max depth logic
+		{jsonDepth: 5, cfgMaxDepth: 6},
+		{jsonDepth: 5, cfgMaxDepth: 5},
+		{jsonDepth: 5, cfgMaxDepth: 4, expectedErr: "max depth"},
+		// Try a large depth without a limit
+		{jsonDepth: 128000, cfgMaxDepth: -1},
+	}
+
+	for _, test := range tests {
+		t.Run(fmt.Sprintf("jsonDepth:%v_cfgMaxDepth:%v", test.jsonDepth, test.cfgMaxDepth), func(t *testing.T) {
+			should := require.New(t)
+			cfg := jsoniter.Config{MaxDepth: test.cfgMaxDepth}.Froze()
+
+			var val interface{}
+			err := cfg.Unmarshal(deepJSON(test.jsonDepth), &val)
+			if test.expectedErr != "" {
+				should.Error(err)
+				should.Contains(err.Error(), test.expectedErr)
+			} else {
+				should.NoError(err)
+			}
+		})
+	}
+}
+
 func Test_customize_tag_key(t *testing.T) {
 
 	type TestObject struct {

+ 10 - 0
config.go

@@ -11,6 +11,9 @@ import (
 	"github.com/modern-go/reflect2"
 )
 
+// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
+const defaultMaxDepth = 10000
+
 // Config customize how the API should behave.
 // The API is created from Config by Froze.
 type Config struct {
@@ -25,6 +28,7 @@ type Config struct {
 	ValidateJsonRawMessage        bool
 	ObjectFieldMustBeSimpleString bool
 	CaseSensitive                 bool
+	MaxDepth                      int
 }
 
 // API the public interface of this package.
@@ -56,6 +60,7 @@ var ConfigCompatibleWithStandardLibrary = Config{
 	EscapeHTML:             true,
 	SortMapKeys:            true,
 	ValidateJsonRawMessage: true,
+	MaxDepth:               -1, // encoding/json has no max depth (stack overflow at 2581101)
 }.Froze()
 
 // ConfigFastest marshals float with only 6 digits precision
@@ -80,6 +85,7 @@ type frozenConfig struct {
 	streamPool                    *sync.Pool
 	iteratorPool                  *sync.Pool
 	caseSensitive                 bool
+	maxDepth                      int
 }
 
 func (cfg *frozenConfig) initCache() {
@@ -127,6 +133,9 @@ func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
 
 // Froze forge API from config
 func (cfg Config) Froze() API {
+	if cfg.MaxDepth == 0 {
+		cfg.MaxDepth = defaultMaxDepth
+	}
 	api := &frozenConfig{
 		sortMapKeys:                   cfg.SortMapKeys,
 		indentionStep:                 cfg.IndentionStep,
@@ -134,6 +143,7 @@ func (cfg Config) Froze() API {
 		onlyTaggedField:               cfg.OnlyTaggedField,
 		disallowUnknownFields:         cfg.DisallowUnknownFields,
 		caseSensitive:                 cfg.CaseSensitive,
+		maxDepth:                      cfg.MaxDepth,
 	}
 	api.streamPool = &sync.Pool{
 		New: func() interface{} {

+ 1 - 4
iter.go

@@ -327,12 +327,9 @@ func (iter *Iterator) Read() interface{} {
 	}
 }
 
-// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
-const maxDepth = 10000
-
 func (iter *Iterator) incrementDepth() (success bool) {
 	iter.depth++
-	if iter.depth <= maxDepth {
+	if iter.depth <= iter.cfg.maxDepth || iter.cfg.maxDepth < 0 {
 		return true
 	}
 	iter.ReportError("incrementDepth", "exceeded max depth")