Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f19f855f1 |
@@ -144,13 +144,23 @@ func parseDateTime(b []byte) (time.Time, error) {
|
||||
return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone")
|
||||
}
|
||||
|
||||
// Normalize leap seconds (second=60) to second=59 to prevent overflow
|
||||
// when Go's time.Date normalizes the time. This is necessary because
|
||||
// time.Date(9999, 12, 31, 23, 59, 60, 0, UTC) normalizes to year 10000,
|
||||
// which is outside the valid TOML date range (0000-9999).
|
||||
// See: https://github.com/pelletier/go-toml/issues/1015
|
||||
second := dt.Second
|
||||
if second == 60 {
|
||||
second = 59
|
||||
}
|
||||
|
||||
t := time.Date(
|
||||
dt.Year,
|
||||
time.Month(dt.Month),
|
||||
dt.Day,
|
||||
dt.Hour,
|
||||
dt.Minute,
|
||||
dt.Second,
|
||||
second,
|
||||
dt.Nanosecond,
|
||||
zone)
|
||||
|
||||
|
||||
+7
-1
@@ -94,7 +94,13 @@ type LocalDateTime struct {
|
||||
|
||||
// AsTime converts d into a specific time instance in zone.
|
||||
func (d LocalDateTime) AsTime(zone *time.Location) time.Time {
|
||||
return time.Date(d.Year, time.Month(d.Month), d.Day, d.Hour, d.Minute, d.Second, d.Nanosecond, zone)
|
||||
// Normalize leap seconds (second=60) to second=59 to prevent overflow
|
||||
// when Go's time.Date normalizes the time.
|
||||
second := d.Second
|
||||
if second == 60 {
|
||||
second = 59
|
||||
}
|
||||
return time.Date(d.Year, time.Month(d.Month), d.Day, d.Hour, d.Minute, second, d.Nanosecond, zone)
|
||||
}
|
||||
|
||||
// String returns RFC 3339 representation of d.
|
||||
|
||||
@@ -4242,3 +4242,66 @@ func TestIssue995_SliceNonEmpty_UsesLastElement(t *testing.T) {
|
||||
t.Fatalf("unexpected values in allowlists: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLeapSecondRoundTrip tests that leap seconds (second=60) don't cause
|
||||
// year overflow issues during round-trip marshaling. This reproduces OSS-Fuzz
|
||||
// issue 472183443.
|
||||
func TestLeapSecondRoundTrip(t *testing.T) {
|
||||
// This is the test case from OSS-Fuzz issue #1015
|
||||
input := []byte("s=9999-12-31 23:59:60z")
|
||||
|
||||
var v interface{}
|
||||
err := toml.Unmarshal(input, &v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Marshal back to TOML
|
||||
encoded, err := toml.Marshal(v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Unmarshal again - this should not fail with year overflow
|
||||
var v2 interface{}
|
||||
err = toml.Unmarshal(encoded, &v2)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestLeapSecondVariants tests various leap second edge cases
|
||||
func TestLeapSecondVariants(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
}{
|
||||
{
|
||||
name: "leap second with UTC offset datetime",
|
||||
input: "s=9999-12-31 23:59:60z",
|
||||
},
|
||||
{
|
||||
name: "leap second with positive offset",
|
||||
input: "s=9999-12-31 23:59:60+00:00",
|
||||
},
|
||||
{
|
||||
name: "leap second with negative offset",
|
||||
input: "s=9999-12-31 23:59:60-05:00",
|
||||
},
|
||||
{
|
||||
name: "leap second earlier in year",
|
||||
input: "s=2015-06-30 23:59:60z",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var v interface{}
|
||||
err := toml.Unmarshal([]byte(tc.input), &v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Marshal back to TOML
|
||||
encoded, err := toml.Marshal(v)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Unmarshal again - this should not fail
|
||||
var v2 interface{}
|
||||
err = toml.Unmarshal(encoded, &v2)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user