VYPR
Medium severity5.3OSV Advisory· Published Jan 26, 2026· Updated Apr 15, 2026

CVE-2025-11065

CVE-2025-11065

Description

A flaw was found in github.com/go-viper/mapstructure/v2, in the field processing component using mapstructure.WeakDecode. This vulnerability allows information disclosure through detailed error messages that may leak sensitive input values via malformed user-supplied data processed in security-critical contexts.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/go-viper/mapstructure/v2Go
< 2.4.02.4.0

Affected products

1

Patches

1
742921c9ba28

fix: error message leaks

https://github.com/go-viper/mapstructureMark Sagi-KazarJul 12, 2025via ghsa
4 files changed · +206 30
  • decode_hooks.go+40 23 modified
    @@ -177,7 +177,9 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return time.ParseDuration(data.(string))
    +		d, err := time.ParseDuration(data.(string))
    +
    +		return d, wrapTimeParseDurationError(err)
     	}
     }
     
    @@ -197,7 +199,9 @@ func StringToURLHookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return url.Parse(data.(string))
    +		u, err := url.Parse(data.(string))
    +
    +		return u, wrapUrlError(err)
     	}
     }
     
    @@ -219,7 +223,7 @@ func StringToIPHookFunc() DecodeHookFunc {
     		// Convert it by parsing
     		ip := net.ParseIP(data.(string))
     		if ip == nil {
    -			return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
    +			return net.IP{}, fmt.Errorf("failed parsing ip")
     		}
     
     		return ip, nil
    @@ -243,7 +247,7 @@ func StringToIPNetHookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		_, net, err := net.ParseCIDR(data.(string))
    -		return net, err
    +		return net, wrapNetParseError(err)
     	}
     }
     
    @@ -263,7 +267,9 @@ func StringToTimeHookFunc(layout string) DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return time.Parse(layout, data.(string))
    +		ti, err := time.Parse(layout, data.(string))
    +
    +		return ti, wrapTimeParseError(err)
     	}
     }
     
    @@ -366,7 +372,9 @@ func StringToNetIPAddrHookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return netip.ParseAddr(data.(string))
    +		addr, err := netip.ParseAddr(data.(string))
    +
    +		return addr, wrapNetIPParseAddrError(err)
     	}
     }
     
    @@ -386,7 +394,9 @@ func StringToNetIPAddrPortHookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return netip.ParseAddrPort(data.(string))
    +		addrPort, err := netip.ParseAddrPort(data.(string))
    +
    +		return addrPort, wrapNetIPParseAddrPortError(err)
     	}
     }
     
    @@ -406,7 +416,9 @@ func StringToNetIPPrefixHookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return netip.ParsePrefix(data.(string))
    +		prefix, err := netip.ParsePrefix(data.(string))
    +
    +		return prefix, wrapNetIPParsePrefixError(err)
     	}
     }
     
    @@ -446,7 +458,7 @@ func StringToInt8HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		i64, err := strconv.ParseInt(data.(string), 0, 8)
    -		return int8(i64), err
    +		return int8(i64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -460,7 +472,7 @@ func StringToUint8HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		u64, err := strconv.ParseUint(data.(string), 0, 8)
    -		return uint8(u64), err
    +		return uint8(u64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -474,7 +486,7 @@ func StringToInt16HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		i64, err := strconv.ParseInt(data.(string), 0, 16)
    -		return int16(i64), err
    +		return int16(i64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -488,7 +500,7 @@ func StringToUint16HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		u64, err := strconv.ParseUint(data.(string), 0, 16)
    -		return uint16(u64), err
    +		return uint16(u64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -502,7 +514,7 @@ func StringToInt32HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		i64, err := strconv.ParseInt(data.(string), 0, 32)
    -		return int32(i64), err
    +		return int32(i64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -516,7 +528,7 @@ func StringToUint32HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		u64, err := strconv.ParseUint(data.(string), 0, 32)
    -		return uint32(u64), err
    +		return uint32(u64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -529,7 +541,8 @@ func StringToInt64HookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return strconv.ParseInt(data.(string), 0, 64)
    +		i64, err := strconv.ParseInt(data.(string), 0, 64)
    +		return int64(i64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -542,7 +555,8 @@ func StringToUint64HookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return strconv.ParseUint(data.(string), 0, 64)
    +		u64, err := strconv.ParseUint(data.(string), 0, 64)
    +		return uint64(u64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -556,7 +570,7 @@ func StringToIntHookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		i64, err := strconv.ParseInt(data.(string), 0, 0)
    -		return int(i64), err
    +		return int(i64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -570,7 +584,7 @@ func StringToUintHookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		u64, err := strconv.ParseUint(data.(string), 0, 0)
    -		return uint(u64), err
    +		return uint(u64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -584,7 +598,7 @@ func StringToFloat32HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		f64, err := strconv.ParseFloat(data.(string), 32)
    -		return float32(f64), err
    +		return float32(f64), wrapStrconvNumError(err)
     	}
     }
     
    @@ -597,7 +611,8 @@ func StringToFloat64HookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return strconv.ParseFloat(data.(string), 64)
    +		f64, err := strconv.ParseFloat(data.(string), 64)
    +		return f64, wrapStrconvNumError(err)
     	}
     }
     
    @@ -610,7 +625,8 @@ func StringToBoolHookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return strconv.ParseBool(data.(string))
    +		b, err := strconv.ParseBool(data.(string))
    +		return b, wrapStrconvNumError(err)
     	}
     }
     
    @@ -636,7 +652,7 @@ func StringToComplex64HookFunc() DecodeHookFunc {
     
     		// Convert it by parsing
     		c128, err := strconv.ParseComplex(data.(string), 64)
    -		return complex64(c128), err
    +		return complex64(c128), wrapStrconvNumError(err)
     	}
     }
     
    @@ -649,6 +665,7 @@ func StringToComplex128HookFunc() DecodeHookFunc {
     		}
     
     		// Convert it by parsing
    -		return strconv.ParseComplex(data.(string), 128)
    +		c128, err := strconv.ParseComplex(data.(string), 128)
    +		return c128, wrapStrconvNumError(err)
     	}
     }
    
  • decode_hooks_test.go+3 2 modified
    @@ -1305,6 +1305,7 @@ func (u *unmarshaler) UnmarshalText(text []byte) error {
     
     func TestErrorLeakageDecodeHook(t *testing.T) {
     	u := unmarshaler{}
    +	_ = u
     
     	cases := []struct {
     		value         interface{}
    @@ -1329,9 +1330,9 @@ func TestErrorLeakageDecodeHook(t *testing.T) {
     		{[]uint8{0x00}, "string", WeaklyTypedHook, true},
     		{uint(0), "string", WeaklyTypedHook, true},
     		{struct{}{}, struct{}{}, RecursiveStructToMapHookFunc(), true},
    -		{"testing", u, TextUnmarshallerHookFunc(), false},
    -		// case 15
     		{"testing", netip.Addr{}, StringToNetIPAddrHookFunc(), false},
    +		// {"testing", u, TextUnmarshallerHookFunc(), false},
    +		// case 15
     		{"testing:testing", netip.AddrPort{}, StringToNetIPAddrPortHookFunc(), false},
     		{"testing", netip.Prefix{}, StringToNetIPPrefixHookFunc(), false},
     		{"testing", int8(0), StringToInt8HookFunc(), false},
    
  • errors.go+158 0 modified
    @@ -1,8 +1,14 @@
     package mapstructure
     
     import (
    +	"errors"
     	"fmt"
    +	"net"
    +	"net/url"
     	"reflect"
    +	"strconv"
    +	"strings"
    +	"time"
     )
     
     // Error interface is implemented by all errors emitted by mapstructure.
    @@ -72,3 +78,155 @@ func (e *UnconvertibleTypeError) Error() string {
     }
     
     func (*UnconvertibleTypeError) mapstructure() {}
    +
    +func wrapStrconvNumError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	if err, ok := err.(*strconv.NumError); ok {
    +		return &strconvNumError{Err: err}
    +	}
    +
    +	return err
    +}
    +
    +type strconvNumError struct {
    +	Err *strconv.NumError
    +}
    +
    +func (e *strconvNumError) Error() string {
    +	return "strconv." + e.Err.Func + ": " + e.Err.Err.Error()
    +}
    +
    +func (e *strconvNumError) Unwrap() error { return e.Err }
    +
    +func wrapUrlError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	if err, ok := err.(*url.Error); ok {
    +		return &urlError{Err: err}
    +	}
    +
    +	return err
    +}
    +
    +type urlError struct {
    +	Err *url.Error
    +}
    +
    +func (e *urlError) Error() string {
    +	return fmt.Sprintf("%s", e.Err.Err)
    +}
    +
    +func (e *urlError) Unwrap() error { return e.Err }
    +
    +func wrapNetParseError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	if err, ok := err.(*net.ParseError); ok {
    +		return &netParseError{Err: err}
    +	}
    +
    +	return err
    +}
    +
    +type netParseError struct {
    +	Err *net.ParseError
    +}
    +
    +func (e *netParseError) Error() string {
    +	return "invalid " + e.Err.Type
    +}
    +
    +func (e *netParseError) Unwrap() error { return e.Err }
    +
    +func wrapTimeParseError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	if err, ok := err.(*time.ParseError); ok {
    +		return &timeParseError{Err: err}
    +	}
    +
    +	return err
    +}
    +
    +type timeParseError struct {
    +	Err *time.ParseError
    +}
    +
    +func (e *timeParseError) Error() string {
    +	if e.Err.Message == "" {
    +		return fmt.Sprintf("parsing time as %q: cannot parse as %q", e.Err.Layout, e.Err.LayoutElem)
    +	}
    +
    +	return "parsing time " + e.Err.Message
    +}
    +
    +func (e *timeParseError) Unwrap() error { return e.Err }
    +
    +func wrapNetIPParseAddrError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	if errMsg := err.Error(); strings.HasPrefix(errMsg, "ParseAddr") {
    +		errPieces := strings.Split(errMsg, ": ")
    +
    +		return fmt.Errorf("ParseAddr: %s", errPieces[len(errPieces)-1])
    +	}
    +
    +	return err
    +}
    +
    +func wrapNetIPParseAddrPortError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	errMsg := err.Error()
    +	if strings.HasPrefix(errMsg, "invalid port ") {
    +		return errors.New("invalid port")
    +	} else if strings.HasPrefix(errMsg, "invalid ip:port ") {
    +		return errors.New("invalid ip:port")
    +	}
    +
    +	return err
    +}
    +
    +func wrapNetIPParsePrefixError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	if errMsg := err.Error(); strings.HasPrefix(errMsg, "netip.ParsePrefix") {
    +		errPieces := strings.Split(errMsg, ": ")
    +
    +		return fmt.Errorf("netip.ParsePrefix: %s", errPieces[len(errPieces)-1])
    +	}
    +
    +	return err
    +}
    +
    +func wrapTimeParseDurationError(err error) error {
    +	if err == nil {
    +		return nil
    +	}
    +
    +	errMsg := err.Error()
    +	if strings.HasPrefix(errMsg, "time: unknown unit ") {
    +		return errors.New("time: unknown unit")
    +	} else if strings.HasPrefix(errMsg, "time: ") {
    +		idx := strings.LastIndex(errMsg, " ")
    +
    +		return errors.New(errMsg[:idx])
    +	}
    +
    +	return err
    +}
    
  • mapstructure.go+5 5 modified
    @@ -724,7 +724,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
     			return newDecodeError(name, &ParseError{
     				Expected: val,
     				Value:    data,
    -				Err:      err,
    +				Err:      wrapStrconvNumError(err),
     			})
     		}
     	case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
    @@ -795,7 +795,7 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
     			return newDecodeError(name, &ParseError{
     				Expected: val,
     				Value:    data,
    -				Err:      err,
    +				Err:      wrapStrconvNumError(err),
     			})
     		}
     	case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
    @@ -805,7 +805,7 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
     			return newDecodeError(name, &ParseError{
     				Expected: val,
     				Value:    data,
    -				Err:      err,
    +				Err:      wrapStrconvNumError(err),
     			})
     		}
     		val.SetUint(i)
    @@ -842,7 +842,7 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
     			return newDecodeError(name, &ParseError{
     				Expected: val,
     				Value:    data,
    -				Err:      err,
    +				Err:      wrapStrconvNumError(err),
     			})
     		}
     	default:
    @@ -886,7 +886,7 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value)
     			return newDecodeError(name, &ParseError{
     				Expected: val,
     				Value:    data,
    -				Err:      err,
    +				Err:      wrapStrconvNumError(err),
     			})
     		}
     	case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.