Skip to content

Commit 2edc61f

Browse files
pelletierclaude
andauthored
Fix panic when unmarshaling datetime values to incompatible types (#1028) (#1029)
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 <[email protected]>
1 parent 4a1b05c commit 2edc61f

2 files changed

Lines changed: 55 additions & 9 deletions

File tree

unmarshaler.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,9 @@ func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error
852852
return err
853853
}
854854

855+
if v.Kind() != reflect.Interface && v.Type() != timeType {
856+
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("datetime", v.Type()))
857+
}
855858
v.Set(reflect.ValueOf(dt))
856859
return nil
857860
}
@@ -862,14 +865,14 @@ func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) erro
862865
return err
863866
}
864867

868+
if v.Kind() != reflect.Interface && v.Type() != timeType {
869+
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local date", v.Type()))
870+
}
865871
if v.Type() == timeType {
866-
cast := ld.AsTime(time.Local)
867-
v.Set(reflect.ValueOf(cast))
872+
v.Set(reflect.ValueOf(ld.AsTime(time.Local)))
868873
return nil
869874
}
870-
871875
v.Set(reflect.ValueOf(ld))
872-
873876
return nil
874877
}
875878

@@ -883,6 +886,9 @@ func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) erro
883886
return unstable.NewParserError(rest, "extra characters at the end of a local time")
884887
}
885888

889+
if v.Kind() != reflect.Interface {
890+
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local time", v.Type()))
891+
}
886892
v.Set(reflect.ValueOf(lt))
887893
return nil
888894
}
@@ -897,15 +903,14 @@ func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value)
897903
return unstable.NewParserError(rest, "extra characters at the end of a local date time")
898904
}
899905

906+
if v.Kind() != reflect.Interface && v.Type() != timeType {
907+
return unstable.NewParserError(d.p.Raw(value.Raw), "%s", d.typeMismatchString("local datetime", v.Type()))
908+
}
900909
if v.Type() == timeType {
901-
cast := ldt.AsTime(time.Local)
902-
903-
v.Set(reflect.ValueOf(cast))
910+
v.Set(reflect.ValueOf(ldt.AsTime(time.Local)))
904911
return nil
905912
}
906-
907913
v.Set(reflect.ValueOf(ldt))
908-
909914
return nil
910915
}
911916

unmarshaler_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4344,3 +4344,44 @@ value = "b"
43444344
},
43454345
}, cfg)
43464346
}
4347+
4348+
func TestIssue1028(t *testing.T) {
4349+
// Datetime values assigned to incompatible types should return an error,
4350+
// not panic.
4351+
4352+
type Item struct {
4353+
Name string `toml:"name"`
4354+
}
4355+
4356+
type Config struct {
4357+
Items map[string]Item `toml:"items"`
4358+
}
4359+
4360+
// Error: "cannot decode TOML datetime into struct field Config.Items of type map[string]Item"
4361+
t.Run("OffsetDateTime", func(t *testing.T) {
4362+
var c Config
4363+
err := toml.Unmarshal([]byte(`items = 2023-01-01T10:20:30Z`), &c)
4364+
assert.Error(t, err)
4365+
})
4366+
4367+
// Error: "cannot decode TOML local datetime into struct field Config.Items of type map[string]Item"
4368+
t.Run("LocalDateTime", func(t *testing.T) {
4369+
var c Config
4370+
err := toml.Unmarshal([]byte(`items = 2023-01-01T10:20:30`), &c)
4371+
assert.Error(t, err)
4372+
})
4373+
4374+
// Error: "cannot decode TOML local date into struct field Config.Items of type map[string]Item"
4375+
t.Run("LocalDate", func(t *testing.T) {
4376+
var c Config
4377+
err := toml.Unmarshal([]byte(`items = 2023-01-01`), &c)
4378+
assert.Error(t, err)
4379+
})
4380+
4381+
// Error: "cannot decode TOML local time into struct field Config.Items of type map[string]Item"
4382+
t.Run("LocalTime", func(t *testing.T) {
4383+
var c Config
4384+
err := toml.Unmarshal([]byte(`items = 10:20:30`), &c)
4385+
assert.Error(t, err)
4386+
})
4387+
}

0 commit comments

Comments
 (0)