e5.0.0 Tests added, bug fixes.
This commit is contained in:
48
decode.go
48
decode.go
@ -57,7 +57,7 @@ func (c *Config) unmarshal(basePath string, source Source, target any) error {
|
||||
// Create decoder with comprehensive hooks
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: target,
|
||||
TagName: "toml",
|
||||
TagName: c.tagName,
|
||||
WeaklyTypedInput: true,
|
||||
DecodeHook: c.getDecodeHook(),
|
||||
ZeroFields: true,
|
||||
@ -77,16 +77,16 @@ func (c *Config) unmarshal(basePath string, source Source, target any) error {
|
||||
// getDecodeHook returns the composite decode hook for all type conversions
|
||||
func (c *Config) getDecodeHook() mapstructure.DecodeHookFunc {
|
||||
return mapstructure.ComposeDecodeHookFunc(
|
||||
// Standard hooks
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
|
||||
// Network types
|
||||
stringToNetIPHookFunc(),
|
||||
stringToNetIPNetHookFunc(),
|
||||
stringToURLHookFunc(),
|
||||
|
||||
// Standard hooks
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
|
||||
// Custom application hooks
|
||||
c.customDecodeHook(),
|
||||
)
|
||||
@ -94,7 +94,7 @@ func (c *Config) getDecodeHook() mapstructure.DecodeHookFunc {
|
||||
|
||||
// stringToNetIPHookFunc handles net.IP conversion
|
||||
func stringToNetIPHookFunc() mapstructure.DecodeHookFunc {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
@ -120,27 +120,28 @@ func stringToNetIPHookFunc() mapstructure.DecodeHookFunc {
|
||||
|
||||
// stringToNetIPNetHookFunc handles net.IPNet conversion
|
||||
func stringToNetIPNetHookFunc() mapstructure.DecodeHookFunc {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if t != reflect.TypeOf(net.IPNet{}) && t != reflect.TypeOf(&net.IPNet{}) {
|
||||
isPtr := t.Kind() == reflect.Ptr
|
||||
targetType := t
|
||||
if isPtr {
|
||||
targetType = t.Elem()
|
||||
}
|
||||
if targetType != reflect.TypeOf(net.IPNet{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
str := data.(string)
|
||||
// SECURITY: Validate CIDR format
|
||||
if len(str) > 49 { // Max IPv6 CIDR length
|
||||
return nil, fmt.Errorf("invalid CIDR length: %d", len(str))
|
||||
}
|
||||
|
||||
_, ipnet, err := net.ParseCIDR(str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid CIDR: %w", err)
|
||||
}
|
||||
|
||||
if t == reflect.TypeOf(&net.IPNet{}) {
|
||||
if isPtr {
|
||||
return ipnet, nil
|
||||
}
|
||||
return *ipnet, nil
|
||||
@ -149,27 +150,28 @@ func stringToNetIPNetHookFunc() mapstructure.DecodeHookFunc {
|
||||
|
||||
// stringToURLHookFunc handles url.URL conversion
|
||||
func stringToURLHookFunc() mapstructure.DecodeHookFunc {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if t != reflect.TypeOf(url.URL{}) && t != reflect.TypeOf(&url.URL{}) {
|
||||
isPtr := t.Kind() == reflect.Ptr
|
||||
targetType := t
|
||||
if isPtr {
|
||||
targetType = t.Elem()
|
||||
}
|
||||
if targetType != reflect.TypeOf(url.URL{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
str := data.(string)
|
||||
// SECURITY: Validate URL length to prevent DoS
|
||||
if len(str) > 2048 {
|
||||
return nil, fmt.Errorf("URL too long: %d bytes", len(str))
|
||||
}
|
||||
|
||||
u, err := url.Parse(str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid URL: %w", err)
|
||||
}
|
||||
|
||||
if t == reflect.TypeOf(&url.URL{}) {
|
||||
if isPtr {
|
||||
return u, nil
|
||||
}
|
||||
return *u, nil
|
||||
@ -178,7 +180,7 @@ func stringToURLHookFunc() mapstructure.DecodeHookFunc {
|
||||
|
||||
// customDecodeHook allows for application-specific type conversions
|
||||
func (c *Config) customDecodeHook() mapstructure.DecodeHookFunc {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||
return func(f reflect.Type, t reflect.Type, data any) (any, error) {
|
||||
// SECURITY: Add custom validation for application types here
|
||||
// Example: Rate limit parsing, permission validation, etc.
|
||||
|
||||
@ -215,4 +217,4 @@ func navigateToPath(nested map[string]any, path string) any {
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user