debian-forge-composer/vendor/github.com/perimeterx/marshmallow/unmarshal.go
Sanne Raymaekers b2700903ae go.mod: bump github.com/getkin/kin-openapi to v0.131.0
As deepmap/oapi-codegen didn't work with this newer version, upgrade to
oapi-codegen/oapi-codegen v2.

Mitigating CVE-2025-30153
2025-03-26 11:13:14 +01:00

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('}')
}