mirror of
https://github.com/json-iterator/go.git
synced 2024-11-27 08:30:57 +02:00
Adds MaxDepth config option
Defaults to 10,000 to match the existing maxDepth constant everywhetre, except when using `ConfigCompatibleWithStandardLibrary` - which retains the limitless depth (and causes a stack overflow). Added tests for the new config, and also up to jsoniter's stack overflow limit.
This commit is contained in:
parent
dc11f49689
commit
d296277d5c
@ -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,51 @@ 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"},
|
||||
// Now try some larger values to figure out the limit
|
||||
{jsonDepth: 128000, cfgMaxDepth: -1},
|
||||
{jsonDepth: 512000, cfgMaxDepth: -1},
|
||||
{jsonDepth: 768000, cfgMaxDepth: -1},
|
||||
{jsonDepth: 860367, cfgMaxDepth: -1}, // largest value for jsoniter without stack overflow
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("jsonDepth:%v_cfgMaxDepth:%v", test.jsonDepth, test.cfgMaxDepth), func(t *testing.T) {
|
||||
if testing.Short() && test.jsonDepth >= 512000 {
|
||||
t.Skip("skipping in -short due to large input data")
|
||||
}
|
||||
|
||||
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
config.go
10
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{} {
|
||||
|
5
iter.go
5
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")
|
||||
|
Loading…
Reference in New Issue
Block a user