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.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/go-viper/mapstructure/v2Go | < 2.4.0 | 2.4.0 |
Affected products
1- Range: v1.0.0, v1.1.0, v1.1.1, …
Patches
1742921c9ba28fix: error message leaks
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- github.com/advisories/GHSA-2464-8j7c-4cjmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-11065ghsaADVISORY
- access.redhat.com/security/cve/CVE-2025-11065nvdWEB
- bugzilla.redhat.com/show_bug.cginvdWEB
- github.com/go-viper/mapstructure/commit/742921c9ba2854d27baa64272487fc5075d2c39cnvdWEB
- github.com/go-viper/mapstructure/security/advisories/GHSA-2464-8j7c-4cjmnvdWEB
- pkg.go.dev/vuln/GO-2025-3900ghsaWEB
News mentions
0No linked articles in our index yet.