encoding/jsonpb,textpb: fix handling of duplicate oneof fields
Unmarshaling should fail if multiple fields in the same oneof exists in
the input.
Change-Id: I76efd88681a50c18f3eaf770c9eb48727efb412b
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170517
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/encoding/jsonpb/decode.go b/encoding/jsonpb/decode.go
index 7655054..3d4a068 100644
--- a/encoding/jsonpb/decode.go
+++ b/encoding/jsonpb/decode.go
@@ -153,6 +153,7 @@
var nerr errors.NonFatal
var reqNums set.Ints
var seenNums set.Ints
+ var seenOneofs set.Ints
msgType := m.Type()
knownFields := m.KnownFields()
@@ -237,6 +238,15 @@
return errors.New("%v|%q: %v", fd.FullName(), name, err)
}
} else {
+ // If field is a oneof, check if it has already been set.
+ if od := fd.OneofType(); od != nil {
+ idx := uint64(od.Index())
+ if seenOneofs.Has(idx) {
+ return errors.New("%v: oneof is already set", od.FullName())
+ }
+ seenOneofs.Set(idx)
+ }
+
// Required or optional fields.
if err := o.unmarshalSingular(knownFields, fd); !nerr.Merge(err) {
return errors.New("%v|%q: %v", fd.FullName(), name, err)
diff --git a/encoding/jsonpb/decode_test.go b/encoding/jsonpb/decode_test.go
index 587b2b4..9d50ac1 100644
--- a/encoding/jsonpb/decode_test.go
+++ b/encoding/jsonpb/decode_test.go
@@ -678,6 +678,26 @@
},
},
}, {
+ desc: "oneof set to more than one field",
+ inputMessage: &pb3.Oneofs{},
+ inputText: `{
+ "oneofEnum": "ZERO",
+ "oneofString": "hello"
+}`,
+ wantErr: true,
+ }, {
+ desc: "oneof set to null and value",
+ inputMessage: &pb3.Oneofs{},
+ inputText: `{
+ "oneofEnum": "ZERO",
+ "oneofString": null
+}`,
+ wantMessage: &pb3.Oneofs{
+ Union: &pb3.Oneofs_OneofEnum{
+ OneofEnum: pb3.Enum_ZERO,
+ },
+ },
+ }, {
desc: "repeated null fields",
inputMessage: &pb2.Repeats{},
inputText: `{
diff --git a/encoding/textpb/decode.go b/encoding/textpb/decode.go
index ae2015a..38b512e 100644
--- a/encoding/textpb/decode.go
+++ b/encoding/textpb/decode.go
@@ -104,6 +104,7 @@
xtTypes := knownFields.ExtensionTypes()
var reqNums set.Ints
var seenNums set.Ints
+ var seenOneofs set.Ints
for _, tfield := range tmsg {
tkey := tfield[0]
@@ -158,6 +159,15 @@
return err
}
} else {
+ // If field is a oneof, check if it has already been set.
+ if od := fd.OneofType(); od != nil {
+ idx := uint64(od.Index())
+ if seenOneofs.Has(idx) {
+ return errors.New("oneof %v is already set", od.FullName())
+ }
+ seenOneofs.Set(idx)
+ }
+
// Required or optional fields.
num := uint64(fd.Number())
if seenNums.Has(num) {
diff --git a/encoding/textpb/decode_test.go b/encoding/textpb/decode_test.go
index 2f4045c..2f14029 100644
--- a/encoding/textpb/decode_test.go
+++ b/encoding/textpb/decode_test.go
@@ -523,20 +523,13 @@
},
},
}, {
- desc: "oneof set to last value",
+ desc: "oneof set to more than one field",
inputMessage: &pb3.Oneofs{},
inputText: `
-oneof_nested: {
- s_string: "nested message"
-}
-oneof_enum: TEN
-oneof_string: "wins"
+oneof_enum: ZERO
+oneof_string: "hello"
`,
- wantMessage: &pb3.Oneofs{
- Union: &pb3.Oneofs_OneofString{
- OneofString: "wins",
- },
- },
+ wantErr: true,
}, {
desc: "repeated scalar using same field name",
inputMessage: &pb2.Repeats{},