@@ -77,6 +77,9 @@ func ParseDatetime(s string) (Datetime, error) {
7777 return Datetime {}, fmt .Errorf ("%w: invalid month" , errDatetime )
7878 }
7979 month = 10 * int (rune (s [5 ])- '0' ) + int (rune (s [6 ])- '0' )
80+ if month > 12 {
81+ return Datetime {}, fmt .Errorf ("%w: month is out of range" , errDatetime )
82+ }
8083
8184 if s [7 ] != '-' {
8285 return Datetime {}, fmt .Errorf ("%w: unexpected character %s" , errDatetime , strconv .QuoteRune (rune (s [7 ])))
@@ -88,6 +91,9 @@ func ParseDatetime(s string) (Datetime, error) {
8891 return Datetime {}, fmt .Errorf ("%w: invalid day" , errDatetime )
8992 }
9093 day = 10 * int (rune (s [8 ])- '0' ) + int (rune (s [9 ])- '0' )
94+ if day > 31 {
95+ return Datetime {}, fmt .Errorf ("%w: day is out of range" , errDatetime )
96+ }
9197
9298 // If the length is 10, we only have a date and we're done.
9399 if length == 10 {
@@ -117,6 +123,9 @@ func ParseDatetime(s string) (Datetime, error) {
117123 return Datetime {}, fmt .Errorf ("%w: invalid hour" , errDatetime )
118124 }
119125 hour = 10 * int (rune (s [11 ])- '0' ) + int (rune (s [12 ])- '0' )
126+ if hour > 23 {
127+ return Datetime {}, fmt .Errorf ("%w: hour is out of range" , errDatetime )
128+ }
120129
121130 if s [13 ] != ':' {
122131 return Datetime {}, fmt .Errorf ("%w: unexpected character %s" , errDatetime , strconv .QuoteRune (rune (s [13 ])))
@@ -127,6 +136,9 @@ func ParseDatetime(s string) (Datetime, error) {
127136 return Datetime {}, fmt .Errorf ("%w: invalid minute" , errDatetime )
128137 }
129138 minute = 10 * int (rune (s [14 ])- '0' ) + int (rune (s [15 ])- '0' )
139+ if minute > 59 {
140+ return Datetime {}, fmt .Errorf ("%w: minute is out of range" , errDatetime )
141+ }
130142
131143 if s [16 ] != ':' {
132144 return Datetime {}, fmt .Errorf ("%w: unexpected character %s" , errDatetime , strconv .QuoteRune (rune (s [16 ])))
@@ -137,6 +149,9 @@ func ParseDatetime(s string) (Datetime, error) {
137149 return Datetime {}, fmt .Errorf ("%w: invalid second" , errDatetime )
138150 }
139151 second = 10 * int (rune (s [17 ])- '0' ) + int (rune (s [18 ])- '0' )
152+ if second > 59 {
153+ return Datetime {}, fmt .Errorf ("%w: second is out of range" , errDatetime )
154+ }
140155
141156 // At this point, things are variable.
142157 // 19 can be ., in which case we have milliseconds. (SSS)
@@ -189,9 +204,17 @@ func ParseDatetime(s string) (Datetime, error) {
189204 return Datetime {}, fmt .Errorf ("%w: invalid time zone offset" , errDatetime )
190205 }
191206
192- hh := time .Duration (10 * int64 (rune (s [trailerOffset + 1 ])- '0' )+ int64 (rune (s [trailerOffset + 2 ])- '0' )) * time .Hour
193- mm := time .Duration (10 * int64 (rune (s [trailerOffset + 3 ])- '0' )+ int64 (rune (s [trailerOffset + 4 ])- '0' )) * time .Minute
194- offset = time .Duration (sign ) * (hh + mm )
207+ hh := time .Duration (10 * int64 (rune (s [trailerOffset + 1 ])- '0' ) + int64 (rune (s [trailerOffset + 2 ])- '0' ))
208+ mm := time .Duration (10 * int64 (rune (s [trailerOffset + 3 ])- '0' ) + int64 (rune (s [trailerOffset + 4 ])- '0' ))
209+
210+ if hh > 23 {
211+ return Datetime {}, fmt .Errorf ("%w: time zone offset hours are out of range" , errDatetime )
212+ }
213+ if mm > 59 {
214+ return Datetime {}, fmt .Errorf ("%w: time zone offset minutes are out of range" , errDatetime )
215+ }
216+
217+ offset = time .Duration (sign ) * ((hh * time .Hour ) + (mm * time .Minute ))
195218
196219 default :
197220 return Datetime {}, fmt .Errorf ("%w: invalid time zone designator" , errDatetime )
@@ -200,6 +223,14 @@ func ParseDatetime(s string) (Datetime, error) {
200223 t := time .Date (year , time .Month (month ), day ,
201224 hour , minute , second ,
202225 int (time .Duration (milli )* time .Millisecond ), time .UTC )
226+
227+ // Don't allow wrapping: https://github.com/cedar-policy/rfcs/pull/94, which can occur
228+ // because not all months have 31 days, which is our validation range
229+ _ , tmonth , tday := t .Date ()
230+ if time .Month (month ) != tmonth || day != tday {
231+ return Datetime {}, fmt .Errorf ("%w: invalid date" , errDatetime )
232+ }
233+
203234 t = t .Add (offset )
204235 return Datetime {value : t .UnixMilli ()}, nil
205236}
0 commit comments