As deepmap/oapi-codegen didn't work with this newer version, upgrade to oapi-codegen/oapi-codegen v2. Mitigating CVE-2025-30153
383 lines
10 KiB
Go
383 lines
10 KiB
Go
// Copyright 2022 PerimeterX. All rights reserved.
|
|
// Use of this source code is governed by a MIT style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package marshmallow
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/mailru/easyjson/jlexer"
|
|
"reflect"
|
|
)
|
|
|
|
// Unmarshal parses the JSON-encoded object in data and stores the values
|
|
// in the struct pointed to by v and in the returned map.
|
|
// If v is nil or not a pointer to a struct, Unmarshal returns an ErrInvalidValue.
|
|
// If data is not a valid JSON or not a JSON object Unmarshal returns an ErrInvalidInput.
|
|
//
|
|
// Unmarshal follows the rules of json.Unmarshal with the following exceptions:
|
|
// - All input fields are stored in the resulting map, including fields that do not exist in the
|
|
// struct pointed by v.
|
|
// - Unmarshal only operates on JSON object inputs. It will reject all other types of input
|
|
// by returning ErrInvalidInput.
|
|
// - Unmarshal only operates on struct values. It will reject all other types of v by
|
|
// returning ErrInvalidValue.
|
|
// - Unmarshal supports three types of Mode values. Each mode is self documented and affects
|
|
// how Unmarshal behaves.
|
|
func Unmarshal(data []byte, v interface{}, options ...UnmarshalOption) (map[string]interface{}, error) {
|
|
if !isValidValue(v) {
|
|
return nil, ErrInvalidValue
|
|
}
|
|
opts := buildUnmarshalOptions(options)
|
|
useMultipleErrors := opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue
|
|
d := &decoder{options: opts, lexer: &jlexer.Lexer{Data: data, UseMultipleErrors: useMultipleErrors}}
|
|
result := make(map[string]interface{})
|
|
if d.lexer.IsNull() {
|
|
d.lexer.Skip()
|
|
} else if !d.lexer.IsDelim('{') {
|
|
return nil, ErrInvalidInput
|
|
} else {
|
|
d.populateStruct(false, v, result)
|
|
}
|
|
d.lexer.Consumed()
|
|
if useMultipleErrors {
|
|
errors := d.lexer.GetNonFatalErrors()
|
|
if len(errors) == 0 {
|
|
return result, nil
|
|
}
|
|
return result, &MultipleLexerError{Errors: errors}
|
|
}
|
|
err := d.lexer.Error()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
type decoder struct {
|
|
options *unmarshalOptions
|
|
lexer *jlexer.Lexer
|
|
}
|
|
|
|
func (d *decoder) populateStruct(forcePopulate bool, structInstance interface{}, result map[string]interface{}) (interface{}, bool) {
|
|
doPopulate := !d.options.skipPopulateStruct || forcePopulate
|
|
var structValue reflect.Value
|
|
if doPopulate {
|
|
structValue = reflectStructValue(structInstance)
|
|
}
|
|
fields := mapStructFields(structInstance)
|
|
var clone map[string]interface{}
|
|
if d.options.mode == ModeFailOverToOriginalValue {
|
|
clone = make(map[string]interface{}, len(fields))
|
|
}
|
|
d.lexer.Delim('{')
|
|
for !d.lexer.IsDelim('}') {
|
|
key := d.lexer.UnsafeFieldName(false)
|
|
d.lexer.WantColon()
|
|
refInfo, exists := fields[key]
|
|
if exists {
|
|
value, isValidType := d.valueByReflectType(refInfo.t)
|
|
if isValidType {
|
|
if value != nil && doPopulate {
|
|
field := refInfo.field(structValue)
|
|
assignValue(field, value)
|
|
}
|
|
if !d.options.excludeKnownFieldsFromMap {
|
|
if result != nil {
|
|
result[key] = value
|
|
}
|
|
if clone != nil {
|
|
clone[key] = value
|
|
}
|
|
}
|
|
} else {
|
|
switch d.options.mode {
|
|
case ModeFailOnFirstError:
|
|
return nil, false
|
|
case ModeFailOverToOriginalValue:
|
|
if !forcePopulate {
|
|
result[key] = value
|
|
} else {
|
|
clone[key] = value
|
|
d.lexer.WantComma()
|
|
d.drainLexerMap(clone)
|
|
return clone, false
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
value := d.lexer.Interface()
|
|
if result != nil {
|
|
result[key] = value
|
|
}
|
|
if clone != nil {
|
|
clone[key] = value
|
|
}
|
|
}
|
|
d.lexer.WantComma()
|
|
}
|
|
d.lexer.Delim('}')
|
|
return structInstance, true
|
|
}
|
|
|
|
func (d *decoder) valueByReflectType(t reflect.Type) (interface{}, bool) {
|
|
if t.Implements(unmarshalerType) {
|
|
result := reflect.New(t.Elem()).Interface()
|
|
d.valueFromCustomUnmarshaler(result.(json.Unmarshaler))
|
|
return result, true
|
|
}
|
|
if reflect.PtrTo(t).Implements(unmarshalerType) {
|
|
value := reflect.New(t)
|
|
d.valueFromCustomUnmarshaler(value.Interface().(json.Unmarshaler))
|
|
return value.Elem().Interface(), true
|
|
}
|
|
kind := t.Kind()
|
|
if converter := primitiveConverters[kind]; converter != nil {
|
|
v := d.lexer.Interface()
|
|
if v == nil {
|
|
return nil, true
|
|
}
|
|
converted, ok := converter(v)
|
|
if !ok {
|
|
addUnexpectedTypeLexerError(d.lexer, t)
|
|
return v, false
|
|
}
|
|
return converted, true
|
|
}
|
|
switch kind {
|
|
case reflect.Slice:
|
|
return d.buildSlice(t)
|
|
case reflect.Array:
|
|
return d.buildArray(t)
|
|
case reflect.Map:
|
|
return d.buildMap(t)
|
|
case reflect.Struct:
|
|
value, valid := d.buildStruct(t)
|
|
if value == nil {
|
|
return nil, valid
|
|
}
|
|
if !valid {
|
|
return value, false
|
|
}
|
|
return reflect.ValueOf(value).Elem().Interface(), valid
|
|
case reflect.Ptr:
|
|
if t.Elem().Kind() == reflect.Struct {
|
|
return d.buildStruct(t.Elem())
|
|
}
|
|
value, valid := d.valueByReflectType(t.Elem())
|
|
if value == nil {
|
|
return nil, valid
|
|
}
|
|
if !valid {
|
|
return value, false
|
|
}
|
|
result := reflect.New(reflect.TypeOf(value))
|
|
result.Elem().Set(reflect.ValueOf(value))
|
|
return result.Interface(), valid
|
|
}
|
|
addUnsupportedTypeLexerError(d.lexer, t)
|
|
return nil, false
|
|
}
|
|
|
|
func (d *decoder) buildSlice(sliceType reflect.Type) (interface{}, bool) {
|
|
if d.lexer.IsNull() {
|
|
d.lexer.Skip()
|
|
return nil, true
|
|
}
|
|
if !d.lexer.IsDelim('[') {
|
|
addUnexpectedTypeLexerError(d.lexer, sliceType)
|
|
return d.lexer.Interface(), false
|
|
}
|
|
elemType := sliceType.Elem()
|
|
d.lexer.Delim('[')
|
|
var sliceValue reflect.Value
|
|
if !d.lexer.IsDelim(']') {
|
|
sliceValue = reflect.MakeSlice(sliceType, 0, 4)
|
|
} else {
|
|
sliceValue = reflect.MakeSlice(sliceType, 0, 0)
|
|
}
|
|
for !d.lexer.IsDelim(']') {
|
|
current, valid := d.valueByReflectType(elemType)
|
|
if !valid {
|
|
if d.options.mode != ModeFailOverToOriginalValue {
|
|
d.drainLexerArray(nil)
|
|
return nil, true
|
|
}
|
|
result := d.cloneReflectArray(sliceValue, -1)
|
|
result = append(result, current)
|
|
return d.drainLexerArray(result), true
|
|
}
|
|
sliceValue = reflect.Append(sliceValue, safeReflectValue(elemType, current))
|
|
d.lexer.WantComma()
|
|
}
|
|
d.lexer.Delim(']')
|
|
return sliceValue.Interface(), true
|
|
}
|
|
|
|
func (d *decoder) buildArray(arrayType reflect.Type) (interface{}, bool) {
|
|
if d.lexer.IsNull() {
|
|
d.lexer.Skip()
|
|
return nil, true
|
|
}
|
|
if !d.lexer.IsDelim('[') {
|
|
addUnexpectedTypeLexerError(d.lexer, arrayType)
|
|
return d.lexer.Interface(), false
|
|
}
|
|
elemType := arrayType.Elem()
|
|
arrayValue := reflect.New(arrayType).Elem()
|
|
d.lexer.Delim('[')
|
|
for i := 0; !d.lexer.IsDelim(']'); i++ {
|
|
current, valid := d.valueByReflectType(elemType)
|
|
if !valid {
|
|
if d.options.mode != ModeFailOverToOriginalValue {
|
|
d.drainLexerArray(nil)
|
|
return nil, true
|
|
}
|
|
result := d.cloneReflectArray(arrayValue, i)
|
|
result = append(result, current)
|
|
return d.drainLexerArray(result), true
|
|
}
|
|
if current != nil {
|
|
arrayValue.Index(i).Set(reflect.ValueOf(current))
|
|
}
|
|
d.lexer.WantComma()
|
|
}
|
|
d.lexer.Delim(']')
|
|
return arrayValue.Interface(), true
|
|
}
|
|
|
|
func (d *decoder) buildMap(mapType reflect.Type) (interface{}, bool) {
|
|
if d.lexer.IsNull() {
|
|
d.lexer.Skip()
|
|
return nil, true
|
|
}
|
|
if !d.lexer.IsDelim('{') {
|
|
addUnexpectedTypeLexerError(d.lexer, mapType)
|
|
return d.lexer.Interface(), false
|
|
}
|
|
d.lexer.Delim('{')
|
|
keyType := mapType.Key()
|
|
valueType := mapType.Elem()
|
|
mapValue := reflect.MakeMap(mapType)
|
|
for !d.lexer.IsDelim('}') {
|
|
key, valid := d.valueByReflectType(keyType)
|
|
if !valid {
|
|
if d.options.mode != ModeFailOverToOriginalValue {
|
|
d.lexer.WantColon()
|
|
d.lexer.Interface()
|
|
d.lexer.WantComma()
|
|
d.drainLexerMap(make(map[string]interface{}))
|
|
return nil, true
|
|
}
|
|
strKey, _ := key.(string)
|
|
d.lexer.WantColon()
|
|
value := d.lexer.Interface()
|
|
result := d.cloneReflectMap(mapValue)
|
|
result[strKey] = value
|
|
d.lexer.WantComma()
|
|
d.drainLexerMap(result)
|
|
return result, true
|
|
}
|
|
d.lexer.WantColon()
|
|
value, valid := d.valueByReflectType(valueType)
|
|
if !valid {
|
|
if d.options.mode != ModeFailOverToOriginalValue {
|
|
d.lexer.WantComma()
|
|
d.drainLexerMap(make(map[string]interface{}))
|
|
return nil, true
|
|
}
|
|
strKey, _ := key.(string)
|
|
result := d.cloneReflectMap(mapValue)
|
|
result[strKey] = value
|
|
d.lexer.WantComma()
|
|
d.drainLexerMap(result)
|
|
return result, true
|
|
}
|
|
mapValue.SetMapIndex(safeReflectValue(keyType, key), safeReflectValue(valueType, value))
|
|
d.lexer.WantComma()
|
|
}
|
|
d.lexer.Delim('}')
|
|
return mapValue.Interface(), true
|
|
}
|
|
|
|
func (d *decoder) buildStruct(structType reflect.Type) (interface{}, bool) {
|
|
if d.lexer.IsNull() {
|
|
d.lexer.Skip()
|
|
return nil, true
|
|
}
|
|
if !d.lexer.IsDelim('{') {
|
|
addUnexpectedTypeLexerError(d.lexer, structType)
|
|
return d.lexer.Interface(), false
|
|
}
|
|
value := reflect.New(structType).Interface()
|
|
handler, ok := asJSONDataHandler(value)
|
|
if !ok {
|
|
return d.populateStruct(true, value, nil)
|
|
}
|
|
data := make(map[string]interface{})
|
|
result, valid := d.populateStruct(true, value, data)
|
|
if !valid {
|
|
return result, false
|
|
}
|
|
err := handler(data)
|
|
if err != nil {
|
|
d.lexer.AddNonFatalError(err)
|
|
return result, false
|
|
}
|
|
return result, true
|
|
}
|
|
|
|
func (d *decoder) valueFromCustomUnmarshaler(unmarshaler json.Unmarshaler) {
|
|
data := d.lexer.Raw()
|
|
if !d.lexer.Ok() {
|
|
return
|
|
}
|
|
err := unmarshaler.UnmarshalJSON(data)
|
|
if err != nil {
|
|
d.lexer.AddNonFatalError(err)
|
|
}
|
|
}
|
|
|
|
func (d *decoder) cloneReflectArray(value reflect.Value, length int) []interface{} {
|
|
if length == -1 {
|
|
length = value.Len()
|
|
}
|
|
result := make([]interface{}, length)
|
|
for i := 0; i < length; i++ {
|
|
result[i] = value.Index(i).Interface()
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (d *decoder) cloneReflectMap(mapValue reflect.Value) map[string]interface{} {
|
|
l := mapValue.Len()
|
|
result := make(map[string]interface{}, l)
|
|
for _, key := range mapValue.MapKeys() {
|
|
value := mapValue.MapIndex(key)
|
|
strKey, _ := key.Interface().(string)
|
|
result[strKey] = value.Interface()
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (d *decoder) drainLexerArray(target []interface{}) interface{} {
|
|
d.lexer.WantComma()
|
|
for !d.lexer.IsDelim(']') {
|
|
current := d.lexer.Interface()
|
|
target = append(target, current)
|
|
d.lexer.WantComma()
|
|
}
|
|
d.lexer.Delim(']')
|
|
return target
|
|
}
|
|
|
|
func (d *decoder) drainLexerMap(target map[string]interface{}) {
|
|
for !d.lexer.IsDelim('}') {
|
|
key := d.lexer.String()
|
|
d.lexer.WantColon()
|
|
value := d.lexer.Interface()
|
|
target[key] = value
|
|
d.lexer.WantComma()
|
|
}
|
|
d.lexer.Delim('}')
|
|
}
|