fix: reject an out-of-range date-time offset
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+14
@@ -6,6 +6,7 @@ package interpres
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -78,6 +79,12 @@ var dateTimeShape = regexp.MustCompile(
|
|||||||
`|^\d{2}:\d{2}:\d{2}(\.\d+)?$`,
|
`|^\d{2}:\d{2}:\d{2}(\.\d+)?$`,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// offsetBounds extracts the numeric offset of a date-time. The ABNF bounds it
|
||||||
|
// to 00:00 through 23:59, but time.Parse accepts values outside that range
|
||||||
|
// and rolls them over (for example "+00:60" becomes "+01:00"), so the bounds
|
||||||
|
// are enforced here.
|
||||||
|
var offsetBounds = regexp.MustCompile(`([+-])(\d{2}):(\d{2})$`)
|
||||||
|
|
||||||
// parseDateTime classifies and parses a bare token as a TOML date-time value.
|
// parseDateTime classifies and parses a bare token as a TOML date-time value.
|
||||||
// It returns the decoded value (time.Time, LocalDateTime, LocalDate, or
|
// It returns the decoded value (time.Time, LocalDateTime, LocalDate, or
|
||||||
// LocalTime) and whether the token was a date-time at all.
|
// LocalTime) and whether the token was a date-time at all.
|
||||||
@@ -91,6 +98,13 @@ func parseDateTime(tok string) (any, bool) {
|
|||||||
if !dateTimeShape.MatchString(tok) {
|
if !dateTimeShape.MatchString(tok) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
if m := offsetBounds.FindStringSubmatch(tok); m != nil {
|
||||||
|
hour, _ := strconv.Atoi(m[2])
|
||||||
|
minute, _ := strconv.Atoi(m[3])
|
||||||
|
if hour > 23 || minute > 59 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
// The ABNF accepts lowercase "t"/"z"; time.Parse only matches uppercase.
|
// The ABNF accepts lowercase "t"/"z"; time.Parse only matches uppercase.
|
||||||
norm := strings.ToUpper(tok)
|
norm := strings.ToUpper(tok)
|
||||||
for _, layout := range offsetDateTimeLayouts {
|
for _, layout := range offsetDateTimeLayouts {
|
||||||
|
|||||||
@@ -380,6 +380,34 @@ func TestRejectsInvalidNumbers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseRejectsOffsetOutOfRange(t *testing.T) {
|
||||||
|
for _, tok := range []string{
|
||||||
|
"1979-05-27T07:32:00+00:60",
|
||||||
|
"1979-05-27T07:32:00-00:99",
|
||||||
|
"1979-05-27T07:32:00+24:00",
|
||||||
|
"1979-05-27T07:32:00+99:99",
|
||||||
|
} {
|
||||||
|
if _, err := Parse([]byte("v = " + tok + "\n")); err == nil {
|
||||||
|
t.Errorf("%q: expected an error, got none", tok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAcceptsOffsetBounds(t *testing.T) {
|
||||||
|
tree, err := Parse([]byte("a = 1979-05-27T07:32:00+23:59\nb = 1979-05-27T07:32:00-23:59\n"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parse: %v", err)
|
||||||
|
}
|
||||||
|
a := tree["a"].(time.Time)
|
||||||
|
if _, offset := a.Zone(); offset != 23*3600+59*60 {
|
||||||
|
t.Fatalf("a offset = %d, want %d", offset, 23*3600+59*60)
|
||||||
|
}
|
||||||
|
b := tree["b"].(time.Time)
|
||||||
|
if _, offset := b.Zone(); offset != -(23*3600 + 59*60) {
|
||||||
|
t.Fatalf("b offset = %d", offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAcceptsNumberEdgeCases(t *testing.T) {
|
func TestAcceptsNumberEdgeCases(t *testing.T) {
|
||||||
cases := map[string]any{
|
cases := map[string]any{
|
||||||
"0": int64(0),
|
"0": int64(0),
|
||||||
|
|||||||
Reference in New Issue
Block a user