From f930b1dc76e8ff950a69a8bd945562261d045c64 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Thu, 25 Aug 2022 11:32:03 -0700 Subject: [PATCH] encoding/protojson: fix parsing of google.protobuf.Timestamp The Timestamp message uses a subset of RFC 3339. RFC 3339, section 5.6 specifies that the subsecond field can have any non-zero number of digits. On the other hand, the protobuf documentation specifies that Timestamp uses RFC 3339 with a few restrictions. In other words, protobuf does NOT use RFC 3339, but rather a subset of it. An upstream change https://go.dev/cl/425037 modifies the time package to be internally consistent about handling of extra subsecond digits allowing it to be more in line with RFC 3339. Make a corresponding change here to ensure we remain compliant with protobuf's restricted use of RFC 3339. Change-Id: Ic145c68492fb41a5f7b79b653f3246dd9091d5d8 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/425554 Reviewed-by: Damien Neil Reviewed-by: Lasse Folger --- encoding/protojson/well_known_types.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/encoding/protojson/well_known_types.go b/encoding/protojson/well_known_types.go index c85f8469..6c37d417 100644 --- a/encoding/protojson/well_known_types.go +++ b/encoding/protojson/well_known_types.go @@ -814,16 +814,22 @@ func (d decoder) unmarshalTimestamp(m protoreflect.Message) error { return d.unexpectedTokenError(tok) } - t, err := time.Parse(time.RFC3339Nano, tok.ParsedString()) + s := tok.ParsedString() + t, err := time.Parse(time.RFC3339Nano, s) if err != nil { return d.newError(tok.Pos(), "invalid %v value %v", genid.Timestamp_message_fullname, tok.RawString()) } - // Validate seconds. No need to validate nanos because time.Parse would have - // covered that already. + // Validate seconds. secs := t.Unix() if secs < minTimestampSeconds || secs > maxTimestampSeconds { return d.newError(tok.Pos(), "%v value out of range: %v", genid.Timestamp_message_fullname, tok.RawString()) } + // Validate subseconds. + i := strings.LastIndexByte(s, '.') // start of subsecond field + j := strings.LastIndexAny(s, "Z-+") // start of timezone field + if i >= 0 && j >= i && j-i > len(".999999999") { + return d.newError(tok.Pos(), "invalid %v value %v", genid.Timestamp_message_fullname, tok.RawString()) + } fds := m.Descriptor().Fields() fdSeconds := fds.ByNumber(genid.Timestamp_Seconds_field_number)