Return a type mismatch error instead of panicking when datetime values (DateTime, LocalDate, LocalTime, LocalDateTime) are unmarshaled into incompatible Go types. This makes the decoder safer for processing untrusted TOML input. https://claude.ai/code/session_011jwvtDS5M2KncLrqJpgMr5 Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+14
-9
@@ -852,6 +852,9 @@ func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Interface && v.Type() != timeType {
|
||||||
|
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("datetime", v.Type()))
|
||||||
|
}
|
||||||
v.Set(reflect.ValueOf(dt))
|
v.Set(reflect.ValueOf(dt))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -862,14 +865,14 @@ func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Interface && v.Type() != timeType {
|
||||||
|
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local date", v.Type()))
|
||||||
|
}
|
||||||
if v.Type() == timeType {
|
if v.Type() == timeType {
|
||||||
cast := ld.AsTime(time.Local)
|
v.Set(reflect.ValueOf(ld.AsTime(time.Local)))
|
||||||
v.Set(reflect.ValueOf(cast))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Set(reflect.ValueOf(ld))
|
v.Set(reflect.ValueOf(ld))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,6 +886,9 @@ func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) erro
|
|||||||
return unstable.NewParserError(rest, "extra characters at the end of a local time")
|
return unstable.NewParserError(rest, "extra characters at the end of a local time")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Interface {
|
||||||
|
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local time", v.Type()))
|
||||||
|
}
|
||||||
v.Set(reflect.ValueOf(lt))
|
v.Set(reflect.ValueOf(lt))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -897,15 +903,14 @@ func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value)
|
|||||||
return unstable.NewParserError(rest, "extra characters at the end of a local date time")
|
return unstable.NewParserError(rest, "extra characters at the end of a local date time")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Interface && v.Type() != timeType {
|
||||||
|
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local datetime", v.Type()))
|
||||||
|
}
|
||||||
if v.Type() == timeType {
|
if v.Type() == timeType {
|
||||||
cast := ldt.AsTime(time.Local)
|
v.Set(reflect.ValueOf(ldt.AsTime(time.Local)))
|
||||||
|
|
||||||
v.Set(reflect.ValueOf(cast))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Set(reflect.ValueOf(ldt))
|
v.Set(reflect.ValueOf(ldt))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4344,3 +4344,44 @@ value = "b"
|
|||||||
},
|
},
|
||||||
}, cfg)
|
}, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue1028(t *testing.T) {
|
||||||
|
// Datetime values assigned to incompatible types should return an error,
|
||||||
|
// not panic.
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
Name string `toml:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Items map[string]Item `toml:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error: "cannot decode TOML datetime into struct field Config.Items of type map[string]Item"
|
||||||
|
t.Run("OffsetDateTime", func(t *testing.T) {
|
||||||
|
var c Config
|
||||||
|
err := toml.Unmarshal([]byte(`items = 2023-01-01T10:20:30Z`), &c)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Error: "cannot decode TOML local datetime into struct field Config.Items of type map[string]Item"
|
||||||
|
t.Run("LocalDateTime", func(t *testing.T) {
|
||||||
|
var c Config
|
||||||
|
err := toml.Unmarshal([]byte(`items = 2023-01-01T10:20:30`), &c)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Error: "cannot decode TOML local date into struct field Config.Items of type map[string]Item"
|
||||||
|
t.Run("LocalDate", func(t *testing.T) {
|
||||||
|
var c Config
|
||||||
|
err := toml.Unmarshal([]byte(`items = 2023-01-01`), &c)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Error: "cannot decode TOML local time into struct field Config.Items of type map[string]Item"
|
||||||
|
t.Run("LocalTime", func(t *testing.T) {
|
||||||
|
var c Config
|
||||||
|
err := toml.Unmarshal([]byte(`items = 10:20:30`), &c)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user