From f4d2925220ff248512b2b22a36e71acfc67467cf Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 12 Mar 2021 17:28:04 +0100 Subject: [PATCH] go.mod: github.com/buger/jsonparser v1.1.1 Fix CVE-2020-35381 Signed-off-by: Sebastiaan van Stijn --- go.mod | 2 +- go.sum | 3 +- vendor/github.com/buger/jsonparser/.gitignore | 2 + .../github.com/buger/jsonparser/.travis.yml | 10 +- vendor/github.com/buger/jsonparser/README.md | 4 +- vendor/github.com/buger/jsonparser/bytes.go | 33 ++- .../buger/jsonparser/bytes_unsafe.go | 14 +- vendor/github.com/buger/jsonparser/fuzz.go | 117 +++++++++ vendor/github.com/buger/jsonparser/go.mod | 4 + vendor/github.com/buger/jsonparser/go.sum | 0 .../buger/jsonparser/oss-fuzz-build.sh | 47 ++++ vendor/github.com/buger/jsonparser/parser.go | 234 ++++++++++++------ vendor/modules.txt | 2 +- 13 files changed, 382 insertions(+), 90 deletions(-) create mode 100644 vendor/github.com/buger/jsonparser/fuzz.go create mode 100644 vendor/github.com/buger/jsonparser/go.mod create mode 100644 vendor/github.com/buger/jsonparser/go.sum create mode 100644 vendor/github.com/buger/jsonparser/oss-fuzz-build.sh diff --git a/go.mod b/go.mod index a362a06e..4675d36e 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.14 require ( github.com/Microsoft/hcsshim v0.8.16 github.com/alexflint/go-filemutex v1.1.0 - github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 + github.com/buger/jsonparser v1.1.1 github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a github.com/coreos/go-iptables v0.5.0 github.com/coreos/go-systemd/v22 v22.2.0 diff --git a/go.sum b/go.sum index 4439ffc0..2d6f25f9 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,9 @@ github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= diff --git a/vendor/github.com/buger/jsonparser/.gitignore b/vendor/github.com/buger/jsonparser/.gitignore index 9de1b0fa..5598d8a5 100644 --- a/vendor/github.com/buger/jsonparser/.gitignore +++ b/vendor/github.com/buger/jsonparser/.gitignore @@ -5,6 +5,8 @@ *.mprof +.idea + vendor/github.com/buger/goterm/ prof.cpu prof.mem diff --git a/vendor/github.com/buger/jsonparser/.travis.yml b/vendor/github.com/buger/jsonparser/.travis.yml index 5df0419b..dbfb7cf9 100644 --- a/vendor/github.com/buger/jsonparser/.travis.yml +++ b/vendor/github.com/buger/jsonparser/.travis.yml @@ -1,3 +1,11 @@ language: go -go: 1.7 +arch: + - amd64 + - ppc64le +go: + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x script: go test -v ./. diff --git a/vendor/github.com/buger/jsonparser/README.md b/vendor/github.com/buger/jsonparser/README.md index a9ee6022..d7e0ec39 100644 --- a/vendor/github.com/buger/jsonparser/README.md +++ b/vendor/github.com/buger/jsonparser/README.md @@ -1,5 +1,5 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/buger/jsonparser)](https://goreportcard.com/report/github.com/buger/jsonparser) ![License](https://img.shields.io/dub/l/vibe-d.svg) -# Alternative JSON parser for Go (so far fastest) +# Alternative JSON parser for Go (10x times faster standard library) It does not require you to know the structure of the payload (eg. create structs), and allows accessing fields by providing the path to them. It is up to **10 times faster** than standard `encoding/json` package (depending on payload size and usage), **allocates no memory**. See benchmarks below. @@ -61,7 +61,7 @@ jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, off }, "person", "avatars") // Or use can access fields by index! -jsonparser.GetInt("person", "avatars", "[0]", "url") +jsonparser.GetString(data, "person", "avatars", "[0]", "url") // You can use `ObjectEach` helper to iterate objects { "key1":object1, "key2":object2, .... "keyN":objectN } jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error { diff --git a/vendor/github.com/buger/jsonparser/bytes.go b/vendor/github.com/buger/jsonparser/bytes.go index d8e76492..0bb0ff39 100644 --- a/vendor/github.com/buger/jsonparser/bytes.go +++ b/vendor/github.com/buger/jsonparser/bytes.go @@ -1,9 +1,16 @@ package jsonparser -// About 3x faster then strconv.ParseInt because does not check for range error and support only base 10, which is enough for JSON -func parseInt(bytes []byte) (v int64, ok bool) { +import ( + bio "bytes" +) + +// minInt64 '-9223372036854775808' is the smallest representable number in int64 +const minInt64 = `9223372036854775808` + +// About 2x faster then strconv.ParseInt because it only supports base 10, which is enough for JSON +func parseInt(bytes []byte) (v int64, ok bool, overflow bool) { if len(bytes) == 0 { - return 0, false + return 0, false, false } var neg bool = false @@ -12,17 +19,29 @@ func parseInt(bytes []byte) (v int64, ok bool) { bytes = bytes[1:] } + var b int64 = 0 for _, c := range bytes { if c >= '0' && c <= '9' { - v = (10 * v) + int64(c-'0') + b = (10 * v) + int64(c-'0') } else { - return 0, false + return 0, false, false } + if overflow = (b < v); overflow { + break + } + v = b + } + + if overflow { + if neg && bio.Equal(bytes, []byte(minInt64)) { + return b, true, false + } + return 0, false, true } if neg { - return -v, true + return -v, true, false } else { - return v, true + return v, true, false } } diff --git a/vendor/github.com/buger/jsonparser/bytes_unsafe.go b/vendor/github.com/buger/jsonparser/bytes_unsafe.go index d3f523dd..589fea87 100644 --- a/vendor/github.com/buger/jsonparser/bytes_unsafe.go +++ b/vendor/github.com/buger/jsonparser/bytes_unsafe.go @@ -6,6 +6,7 @@ import ( "reflect" "strconv" "unsafe" + "runtime" ) // @@ -32,11 +33,12 @@ func bytesToString(b *[]byte) string { } func StringToBytes(s string) []byte { + b := make([]byte, 0, 0) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := reflect.SliceHeader{ - Data: sh.Data, - Len: sh.Len, - Cap: sh.Len, - } - return *(*[]byte)(unsafe.Pointer(&bh)) + bh.Data = sh.Data + bh.Cap = sh.Len + bh.Len = sh.Len + runtime.KeepAlive(s) + return b } diff --git a/vendor/github.com/buger/jsonparser/fuzz.go b/vendor/github.com/buger/jsonparser/fuzz.go new file mode 100644 index 00000000..854bd11b --- /dev/null +++ b/vendor/github.com/buger/jsonparser/fuzz.go @@ -0,0 +1,117 @@ +package jsonparser + +func FuzzParseString(data []byte) int { + r, err := ParseString(data) + if err != nil || r == "" { + return 0 + } + return 1 +} + +func FuzzEachKey(data []byte) int { + paths := [][]string{ + {"name"}, + {"order"}, + {"nested", "a"}, + {"nested", "b"}, + {"nested2", "a"}, + {"nested", "nested3", "b"}, + {"arr", "[1]", "b"}, + {"arrInt", "[3]"}, + {"arrInt", "[5]"}, + {"nested"}, + {"arr", "["}, + {"a\n", "b\n"}, + } + EachKey(data, func(idx int, value []byte, vt ValueType, err error) {}, paths...) + return 1 +} + +func FuzzDelete(data []byte) int { + Delete(data, "test") + return 1 +} + +func FuzzSet(data []byte) int { + _, err := Set(data, []byte(`"new value"`), "test") + if err != nil { + return 0 + } + return 1 +} + +func FuzzObjectEach(data []byte) int { + _ = ObjectEach(data, func(key, value []byte, valueType ValueType, off int) error { + return nil + }) + return 1 +} + +func FuzzParseFloat(data []byte) int { + _, err := ParseFloat(data) + if err != nil { + return 0 + } + return 1 +} + +func FuzzParseInt(data []byte) int { + _, err := ParseInt(data) + if err != nil { + return 0 + } + return 1 +} + +func FuzzParseBool(data []byte) int { + _, err := ParseBoolean(data) + if err != nil { + return 0 + } + return 1 +} + +func FuzzTokenStart(data []byte) int { + _ = tokenStart(data) + return 1 +} + +func FuzzGetString(data []byte) int { + _, err := GetString(data, "test") + if err != nil { + return 0 + } + return 1 +} + +func FuzzGetFloat(data []byte) int { + _, err := GetFloat(data, "test") + if err != nil { + return 0 + } + return 1 +} + +func FuzzGetInt(data []byte) int { + _, err := GetInt(data, "test") + if err != nil { + return 0 + } + return 1 +} + +func FuzzGetBoolean(data []byte) int { + _, err := GetBoolean(data, "test") + if err != nil { + return 0 + } + return 1 +} + +func FuzzGetUnsafeString(data []byte) int { + _, err := GetUnsafeString(data, "test") + if err != nil { + return 0 + } + return 1 +} diff --git a/vendor/github.com/buger/jsonparser/go.mod b/vendor/github.com/buger/jsonparser/go.mod new file mode 100644 index 00000000..7ede21fb --- /dev/null +++ b/vendor/github.com/buger/jsonparser/go.mod @@ -0,0 +1,4 @@ +module github.com/buger/jsonparser + +go 1.13 + diff --git a/vendor/github.com/buger/jsonparser/go.sum b/vendor/github.com/buger/jsonparser/go.sum new file mode 100644 index 00000000..e69de29b diff --git a/vendor/github.com/buger/jsonparser/oss-fuzz-build.sh b/vendor/github.com/buger/jsonparser/oss-fuzz-build.sh new file mode 100644 index 00000000..c573b0e2 --- /dev/null +++ b/vendor/github.com/buger/jsonparser/oss-fuzz-build.sh @@ -0,0 +1,47 @@ +#!/bin/bash -eu + +git clone https://github.com/dvyukov/go-fuzz-corpus +zip corpus.zip go-fuzz-corpus/json/corpus/* + +cp corpus.zip $OUT/fuzzparsestring_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzParseString fuzzparsestring + +cp corpus.zip $OUT/fuzzeachkey_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzEachKey fuzzeachkey + +cp corpus.zip $OUT/fuzzdelete_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzDelete fuzzdelete + +cp corpus.zip $OUT/fuzzset_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzSet fuzzset + +cp corpus.zip $OUT/fuzzobjecteach_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzObjectEach fuzzobjecteach + +cp corpus.zip $OUT/fuzzparsefloat_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzParseFloat fuzzparsefloat + +cp corpus.zip $OUT/fuzzparseint_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzParseInt fuzzparseint + +cp corpus.zip $OUT/fuzzparsebool_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzParseBool fuzzparsebool + +cp corpus.zip $OUT/fuzztokenstart_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzTokenStart fuzztokenstart + +cp corpus.zip $OUT/fuzzgetstring_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzGetString fuzzgetstring + +cp corpus.zip $OUT/fuzzgetfloat_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzGetFloat fuzzgetfloat + +cp corpus.zip $OUT/fuzzgetint_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzGetInt fuzzgetint + +cp corpus.zip $OUT/fuzzgetboolean_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzGetBoolean fuzzgetboolean + +cp corpus.zip $OUT/fuzzgetunsafestring_seed_corpus.zip +compile_go_fuzzer github.com/buger/jsonparser FuzzGetUnsafeString fuzzgetunsafestring + diff --git a/vendor/github.com/buger/jsonparser/parser.go b/vendor/github.com/buger/jsonparser/parser.go index 7a72c089..14b80bc4 100644 --- a/vendor/github.com/buger/jsonparser/parser.go +++ b/vendor/github.com/buger/jsonparser/parser.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "math" "strconv" ) @@ -17,6 +16,7 @@ var ( MalformedArrayError = errors.New("Value is array, but can't find closing ']' symbol") MalformedObjectError = errors.New("Value looks like object, but can't find closing '}' symbol") MalformedValueError = errors.New("Value looks like Number/Boolean/None, but can't find its end: ',' or '}' symbol") + OverflowIntegerError = errors.New("Value is number, but overflowed while parsing") MalformedStringEscapeError = errors.New("Encountered an invalid escape sequence in a string") ) @@ -97,9 +97,15 @@ func findKeyStart(data []byte, key string) (int, error) { } case '[': - i = blockEnd(data[i:], data[i], ']') + i + end := blockEnd(data[i:], data[i], ']') + if end != -1 { + i = i + end + } case '{': - i = blockEnd(data[i:], data[i], '}') + i + end := blockEnd(data[i:], data[i], '}') + if end != -1 { + i = i + end + } } i++ } @@ -213,6 +219,7 @@ func searchKeys(data []byte, keys ...string) int { i := 0 ln := len(data) lk := len(keys) + lastMatched := true if lk == 0 { return 0 @@ -240,8 +247,8 @@ func searchKeys(data []byte, keys ...string) int { i += valueOffset - // if string is a key, and key level match - if data[i] == ':' && keyLevel == level-1 { + // if string is a key + if data[i] == ':' { if level < 1 { return -1 } @@ -259,18 +266,40 @@ func searchKeys(data []byte, keys ...string) int { keyUnesc = ku } - if equalStr(&keyUnesc, keys[level-1]) { - keyLevel++ - // If we found all keys in path - if keyLevel == lk { - return i + 1 + if level <= len(keys) { + if equalStr(&keyUnesc, keys[level-1]) { + lastMatched = true + + // if key level match + if keyLevel == level-1 { + keyLevel++ + // If we found all keys in path + if keyLevel == lk { + return i + 1 + } + } + } else { + lastMatched = false } + } else { + return -1 } } else { i-- } case '{': - level++ + + // in case parent key is matched then only we will increase the level otherwise can directly + // can move to the end of this block + if !lastMatched { + end := blockEnd(data[i:], '{', '}') + if end == -1 { + return -1 + } + i += end - 1 + } else { + level++ + } case '}': level-- if level == keyLevel { @@ -279,7 +308,11 @@ func searchKeys(data []byte, keys ...string) int { case '[': // If we want to get array element by index if keyLevel == level && keys[level][0] == '[' { - aIdx, err := strconv.Atoi(keys[level][1 : len(keys[level])-1]) + var keyLen = len(keys[level]) + if keyLen < 3 || keys[level][0] != '[' || keys[level][keyLen-1] != ']' { + return -1 + } + aIdx, err := strconv.Atoi(keys[level][1 : keyLen-1]) if err != nil { return -1 } @@ -316,6 +349,8 @@ func searchKeys(data []byte, keys ...string) int { i += arraySkip - 1 } } + case ':': // If encountered, JSON data is malformed + return -1 } i++ @@ -324,14 +359,6 @@ func searchKeys(data []byte, keys ...string) int { return -1 } -var bitwiseFlags []int64 - -func init() { - for i := 0; i < 63; i++ { - bitwiseFlags = append(bitwiseFlags, int64(math.Pow(2, float64(i)))) - } -} - func sameTree(p1, p2 []string) bool { minLen := len(p1) if len(p2) < minLen { @@ -348,7 +375,8 @@ func sameTree(p1, p2 []string) bool { } func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]string) int { - var pathFlags int64 + var x struct{} + pathFlags := make([]bool, len(paths)) var level, pathsMatched, i int ln := len(data) @@ -359,7 +387,6 @@ func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]str } } - var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings pathsBuf := make([]string, maxPath) for i < ln { @@ -393,10 +420,13 @@ func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]str var keyUnesc []byte if !keyEscaped { keyUnesc = key - } else if ku, err := Unescape(key, stackbuf[:]); err != nil { - return -1 } else { - keyUnesc = ku + var stackbuf [unescapeStackBufSize]byte + if ku, err := Unescape(key, stackbuf[:]); err != nil { + return -1 + } else { + keyUnesc = ku + } } if maxPath >= level { @@ -407,23 +437,18 @@ func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]str pathsBuf[level-1] = bytesToString(&keyUnesc) for pi, p := range paths { - if len(p) != level || pathFlags&bitwiseFlags[pi+1] != 0 || !equalStr(&keyUnesc, p[level-1]) || !sameTree(p, pathsBuf[:level]) { + if len(p) != level || pathFlags[pi] || !equalStr(&keyUnesc, p[level-1]) || !sameTree(p, pathsBuf[:level]) { continue } match = pi - i++ pathsMatched++ - pathFlags |= bitwiseFlags[pi+1] + pathFlags[pi] = true - v, dt, of, e := Get(data[i:]) + v, dt, _, e := Get(data[i+1:]) cb(pi, v, dt, e) - if of != -1 { - i += of - } - if pathsMatched == len(paths) { break } @@ -457,8 +482,9 @@ func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]str case '}': level-- case '[': - var arrIdxFlags int64 - var pIdxFlags int64 + var ok bool + arrIdxFlags := make(map[int]struct{}) + pIdxFlags := make([]bool, len(paths)) if level < 0 { cb(-1, nil, Unknown, MalformedJsonError) @@ -466,30 +492,31 @@ func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]str } for pi, p := range paths { - if len(p) < level+1 || pathFlags&bitwiseFlags[pi+1] != 0 || p[level][0] != '[' || !sameTree(p, pathsBuf[:level]) { + if len(p) < level+1 || pathFlags[pi] || p[level][0] != '[' || !sameTree(p, pathsBuf[:level]) { continue } - - aIdx, _ := strconv.Atoi(p[level][1 : len(p[level])-1]) - arrIdxFlags |= bitwiseFlags[aIdx+1] - pIdxFlags |= bitwiseFlags[pi+1] + if len(p[level]) >= 2 { + aIdx, _ := strconv.Atoi(p[level][1 : len(p[level])-1]) + arrIdxFlags[aIdx] = x + pIdxFlags[pi] = true + } } - if arrIdxFlags > 0 { + if len(arrIdxFlags) > 0 { level++ var curIdx int arrOff, _ := ArrayEach(data[i:], func(value []byte, dataType ValueType, offset int, err error) { - if arrIdxFlags&bitwiseFlags[curIdx+1] != 0 { + if _, ok = arrIdxFlags[curIdx]; ok { for pi, p := range paths { - if pIdxFlags&bitwiseFlags[pi+1] != 0 { + if pIdxFlags[pi] { aIdx, _ := strconv.Atoi(p[level-1][1 : len(p[level-1])-1]) if curIdx == aIdx { of := searchKeys(value, p[level:]...) pathsMatched++ - pathFlags |= bitwiseFlags[pi+1] + pathFlags[pi] = true if of != -1 { v, dt, _, e := Get(value[of:]) @@ -568,46 +595,96 @@ var ( ) func createInsertComponent(keys []string, setValue []byte, comma, object bool) []byte { - var buffer bytes.Buffer isIndex := string(keys[0][0]) == "[" + offset := 0 + lk := calcAllocateSpace(keys, setValue, comma, object) + buffer := make([]byte, lk, lk) if comma { - buffer.WriteString(",") + offset += WriteToBuffer(buffer[offset:], ",") } - if isIndex { - buffer.WriteString("[") + if isIndex && !comma { + offset += WriteToBuffer(buffer[offset:], "[") } else { if object { - buffer.WriteString("{") + offset += WriteToBuffer(buffer[offset:], "{") + } + if !isIndex { + offset += WriteToBuffer(buffer[offset:], "\"") + offset += WriteToBuffer(buffer[offset:], keys[0]) + offset += WriteToBuffer(buffer[offset:], "\":") } - buffer.WriteString("\"") - buffer.WriteString(keys[0]) - buffer.WriteString("\":") } for i := 1; i < len(keys); i++ { if string(keys[i][0]) == "[" { - buffer.WriteString("[") + offset += WriteToBuffer(buffer[offset:], "[") } else { - buffer.WriteString("{\"") - buffer.WriteString(keys[i]) - buffer.WriteString("\":") + offset += WriteToBuffer(buffer[offset:], "{\"") + offset += WriteToBuffer(buffer[offset:], keys[i]) + offset += WriteToBuffer(buffer[offset:], "\":") } } - buffer.Write(setValue) + offset += WriteToBuffer(buffer[offset:], string(setValue)) for i := len(keys) - 1; i > 0; i-- { if string(keys[i][0]) == "[" { - buffer.WriteString("]") + offset += WriteToBuffer(buffer[offset:], "]") } else { - buffer.WriteString("}") + offset += WriteToBuffer(buffer[offset:], "}") } } - if isIndex { - buffer.WriteString("]") + if isIndex && !comma { + offset += WriteToBuffer(buffer[offset:], "]") } if object && !isIndex { - buffer.WriteString("}") + offset += WriteToBuffer(buffer[offset:], "}") } - return buffer.Bytes() + return buffer +} + +func calcAllocateSpace(keys []string, setValue []byte, comma, object bool) int { + isIndex := string(keys[0][0]) == "[" + lk := 0 + if comma { + // , + lk += 1 + } + if isIndex && !comma { + // [] + lk += 2 + } else { + if object { + // { + lk += 1 + } + if !isIndex { + // "keys[0]" + lk += len(keys[0]) + 3 + } + } + + + lk += len(setValue) + for i := 1; i < len(keys); i++ { + if string(keys[i][0]) == "[" { + // [] + lk += 2 + } else { + // {"keys[i]":setValue} + lk += len(keys[i]) + 5 + } + } + + if object && !isIndex { + // } + lk += 1 + } + + return lk +} + +func WriteToBuffer(buffer []byte, str string) int { + copy(buffer, str) + return len(str) } /* @@ -687,7 +764,12 @@ func Delete(data []byte, keys ...string) []byte { newOffset = prevTok + 1 } - data = append(data[:newOffset], data[endOffset:]...) + // We have to make a copy here if we don't want to mangle the original data, because byte slices are + // accessed by reference and not by value + dataCopy := make([]byte, len(data)) + copy(dataCopy, data) + data = append(dataCopy[:newOffset], dataCopy[endOffset:]...) + return data } @@ -730,7 +812,7 @@ func Set(data []byte, setValue []byte, keys ...string) (value []byte, err error) if endOffset == -1 { firstToken := nextToken(data) // We can't set a top-level key if data isn't an object - if len(data) == 0 || data[firstToken] != '{' { + if firstToken < 0 || data[firstToken] != '{' { return nil, KeyPathNotFoundError } // Don't need a comma if the input is an empty object @@ -745,7 +827,9 @@ func Set(data []byte, setValue []byte, keys ...string) (value []byte, err error) depthOffset := endOffset if depth != 0 { // if subpath is a non-empty object, add to it - if data[startOffset] == '{' && data[startOffset+1+nextToken(data[startOffset+1:])] != '}' { + // or if subpath is a non-empty array, add to it + if (data[startOffset] == '{' && data[startOffset+1+nextToken(data[startOffset+1:])] != '}') || + (data[startOffset] == '[' && data[startOffset+1+nextToken(data[startOffset+1:])] == '{') && keys[depth:][0][0] == 91 { depthOffset-- startOffset = depthOffset // otherwise, over-write it with a new object @@ -878,7 +962,7 @@ func internalGet(data []byte, keys ...string) (value []byte, dataType ValueType, value = value[1 : len(value)-1] } - return value, dataType, offset, endOffset, nil + return value[:len(value):len(value)], dataType, offset, endOffset, nil } // ArrayEach is used when iterating arrays, accepts a callback function with the same return arguments as `Get`. @@ -887,7 +971,12 @@ func ArrayEach(data []byte, cb func(value []byte, dataType ValueType, offset int return -1, MalformedObjectError } - offset = 1 + nT := nextToken(data) + if nT == -1 { + return -1, MalformedJsonError + } + + offset = nT + 1 if len(keys) > 0 { if offset = searchKeys(data, keys...); offset == -1 { @@ -963,7 +1052,6 @@ func ArrayEach(data []byte, cb func(value []byte, dataType ValueType, offset int // ObjectEach iterates over the key-value pairs of a JSON object, invoking a given callback for each such entry func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string) (err error) { - var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings offset := 0 // Descend to the desired key, if requested @@ -1017,6 +1105,7 @@ func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType Va // Unescape the string if needed if keyEscaped { + var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings if keyUnescaped, err := Unescape(key, stackbuf[:]); err != nil { return MalformedStringEscapeError } else { @@ -1092,7 +1181,7 @@ func GetString(data []byte, keys ...string) (val string, err error) { return "", fmt.Errorf("Value is not a string: %s", string(v)) } - // If no escapes return raw conten + // If no escapes return raw content if bytes.IndexByte(v, '\\') == -1 { return string(v), nil } @@ -1183,7 +1272,10 @@ func ParseFloat(b []byte) (float64, error) { // ParseInt parses a Number ValueType into a Go int64 func ParseInt(b []byte) (int64, error) { - if v, ok := parseInt(b); !ok { + if v, ok, overflow := parseInt(b); !ok { + if overflow { + return 0, OverflowIntegerError + } return 0, MalformedValueError } else { return v, nil diff --git a/vendor/modules.txt b/vendor/modules.txt index 03a3d2b9..68bf39c9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -32,7 +32,7 @@ github.com/Microsoft/hcsshim/osversion # github.com/alexflint/go-filemutex v1.1.0 ## explicit github.com/alexflint/go-filemutex -# github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 +# github.com/buger/jsonparser v1.1.1 ## explicit github.com/buger/jsonparser # github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68