internal/json: json nesting depth check (#1200)
Added a lightweight JSON depth-checking pass before the actual parsing.
This commit is contained in:
parent
2732839dba
commit
b55bcbd610
1 changed files with 99 additions and 0 deletions
|
|
@ -8,11 +8,21 @@ package json
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/segmentio/encoding/json"
|
||||
)
|
||||
|
||||
// defaultMaxDepth is the maximum JSON nesting depth accepted by [Unmarshal].
|
||||
// Realistic MCP payloads nest only a handful of levels, so 1000 is far more
|
||||
// than any legitimate message requires while keeping worst-case parse cost
|
||||
// bounded.
|
||||
const defaultMaxDepth = 1000
|
||||
|
||||
// errMaxDepthExceeded is returned by [Unmarshal] when the input nests deeper than [defaultMaxDepth].
|
||||
var errMaxDepthExceeded = fmt.Errorf("json: exceeded maximum nesting depth of %d", defaultMaxDepth)
|
||||
|
||||
type Decoder struct {
|
||||
dec *json.Decoder
|
||||
}
|
||||
|
|
@ -28,5 +38,47 @@ func (d *Decoder) Decode(v any) error {
|
|||
}
|
||||
|
||||
func Unmarshal(data []byte, v any) error {
|
||||
if err := checkMaxDepth(data, defaultMaxDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
return NewDecoder(bytes.NewReader(data)).Decode(v)
|
||||
}
|
||||
|
||||
// checkMaxDepth scans data once and reports [errMaxDepthExceeded] if the
|
||||
// nesting of JSON objects and arrays exceeds maxDepth. It is a lightweight pass which
|
||||
// tracks '{' and '[' against '}' and ']' while skipping over the contents of strings.
|
||||
func checkMaxDepth(data []byte, maxDepth int) error {
|
||||
var (
|
||||
depth int
|
||||
inString bool
|
||||
escaped bool
|
||||
)
|
||||
for _, b := range data {
|
||||
if inString {
|
||||
switch {
|
||||
case escaped:
|
||||
escaped = false
|
||||
case b == '\\':
|
||||
escaped = true
|
||||
case b == '"':
|
||||
inString = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch b {
|
||||
case '"':
|
||||
inString = true
|
||||
case '{', '[':
|
||||
depth++
|
||||
if depth > maxDepth {
|
||||
return errMaxDepthExceeded
|
||||
}
|
||||
case '}', ']':
|
||||
if depth > 0 {
|
||||
depth--
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue