mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-04-16 08:42:29 +00:00
encoding/protojson: add better validation to FieldMask serialization
For marshaling, apart from already existing check that each item in paths field is reversible, also make sure that string is a valid protobuf name. For unmarshaling, make sure that each resulting item in paths field is a valid protobuf name and input path item does not contain _. The latter check satisfies the conformance test Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter. Fixes golang/protobuf#1141. Change-Id: Iffc278089b20e496b7216d5b8c966b21b70e782d Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/236998 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
This commit is contained in:
parent
596bfbf068
commit
beaa55256c
@ -1903,7 +1903,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
}, {
|
}, {
|
||||||
desc: "FieldMask",
|
desc: "FieldMask",
|
||||||
inputMessage: &fieldmaskpb.FieldMask{},
|
inputMessage: &fieldmaskpb.FieldMask{},
|
||||||
inputText: `"foo,fooBar , foo.barQux ,Foo"`,
|
inputText: `"foo,fooBar,foo.barQux,Foo"`,
|
||||||
wantMessage: &fieldmaskpb.FieldMask{
|
wantMessage: &fieldmaskpb.FieldMask{
|
||||||
Paths: []string{
|
Paths: []string{
|
||||||
"foo",
|
"foo",
|
||||||
@ -1912,11 +1912,31 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
"_foo",
|
"_foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
desc: "FieldMask empty path 1",
|
||||||
|
inputMessage: &fieldmaskpb.FieldMask{},
|
||||||
|
inputText: `"foo,"`,
|
||||||
|
wantErr: `google.protobuf.FieldMask.paths contains invalid path: ""`,
|
||||||
|
}, {
|
||||||
|
desc: "FieldMask empty path 2",
|
||||||
|
inputMessage: &fieldmaskpb.FieldMask{},
|
||||||
|
inputText: `"foo, ,bar"`,
|
||||||
|
wantErr: `google.protobuf.FieldMask.paths contains invalid path: " "`,
|
||||||
|
}, {
|
||||||
|
desc: "FieldMask invalid char 1",
|
||||||
|
inputMessage: &fieldmaskpb.FieldMask{},
|
||||||
|
inputText: `"foo_bar"`,
|
||||||
|
wantErr: `google.protobuf.FieldMask.paths contains invalid path: "foo_bar"`,
|
||||||
|
}, {
|
||||||
|
desc: "FieldMask invalid char 2",
|
||||||
|
inputMessage: &fieldmaskpb.FieldMask{},
|
||||||
|
inputText: `"foo@bar"`,
|
||||||
|
wantErr: `google.protobuf.FieldMask.paths contains invalid path: "foo@bar"`,
|
||||||
}, {
|
}, {
|
||||||
desc: "FieldMask field",
|
desc: "FieldMask field",
|
||||||
inputMessage: &pb2.KnownTypes{},
|
inputMessage: &pb2.KnownTypes{},
|
||||||
inputText: `{
|
inputText: `{
|
||||||
"optFieldmask": "foo, qux.fooBar"
|
"optFieldmask": "foo,qux.fooBar"
|
||||||
}`,
|
}`,
|
||||||
wantMessage: &pb2.KnownTypes{
|
wantMessage: &pb2.KnownTypes{
|
||||||
OptFieldmask: &fieldmaskpb.FieldMask{
|
OptFieldmask: &fieldmaskpb.FieldMask{
|
||||||
|
@ -1507,17 +1507,35 @@ func TestMarshal(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: `"foo,fooBar,foo.barQux,Foo"`,
|
want: `"foo,fooBar,foo.barQux,Foo"`,
|
||||||
}, {
|
}, {
|
||||||
desc: "FieldMask error 1",
|
desc: "FieldMask empty string path",
|
||||||
|
input: &fieldmaskpb.FieldMask{
|
||||||
|
Paths: []string{""},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
desc: "FieldMask path contains spaces only",
|
||||||
|
input: &fieldmaskpb.FieldMask{
|
||||||
|
Paths: []string{" "},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
desc: "FieldMask irreversible error 1",
|
||||||
input: &fieldmaskpb.FieldMask{
|
input: &fieldmaskpb.FieldMask{
|
||||||
Paths: []string{"foo_"},
|
Paths: []string{"foo_"},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
}, {
|
}, {
|
||||||
desc: "FieldMask error 2",
|
desc: "FieldMask irreversible error 2",
|
||||||
input: &fieldmaskpb.FieldMask{
|
input: &fieldmaskpb.FieldMask{
|
||||||
Paths: []string{"foo__bar"},
|
Paths: []string{"foo__bar"},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
desc: "FieldMask invalid char",
|
||||||
|
input: &fieldmaskpb.FieldMask{
|
||||||
|
Paths: []string{"foo@bar"},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
}, {
|
}, {
|
||||||
desc: "Any empty",
|
desc: "Any empty",
|
||||||
input: &anypb.Any{},
|
input: &anypb.Any{},
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
"google.golang.org/protobuf/internal/genid"
|
"google.golang.org/protobuf/internal/genid"
|
||||||
"google.golang.org/protobuf/internal/strs"
|
"google.golang.org/protobuf/internal/strs"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
pref "google.golang.org/protobuf/reflect/protoreflect"
|
pref "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ type marshalFunc func(encoder, pref.Message) error
|
|||||||
|
|
||||||
// wellKnownTypeMarshaler returns a marshal function if the message type
|
// wellKnownTypeMarshaler returns a marshal function if the message type
|
||||||
// has specialized serialization behavior. It returns nil otherwise.
|
// has specialized serialization behavior. It returns nil otherwise.
|
||||||
func wellKnownTypeMarshaler(name protoreflect.FullName) marshalFunc {
|
func wellKnownTypeMarshaler(name pref.FullName) marshalFunc {
|
||||||
if name.Parent() == genid.GoogleProtobuf_package {
|
if name.Parent() == genid.GoogleProtobuf_package {
|
||||||
switch name.Name() {
|
switch name.Name() {
|
||||||
case genid.Any_message_name:
|
case genid.Any_message_name:
|
||||||
@ -62,7 +61,7 @@ type unmarshalFunc func(decoder, pref.Message) error
|
|||||||
|
|
||||||
// wellKnownTypeUnmarshaler returns a unmarshal function if the message type
|
// wellKnownTypeUnmarshaler returns a unmarshal function if the message type
|
||||||
// has specialized serialization behavior. It returns nil otherwise.
|
// has specialized serialization behavior. It returns nil otherwise.
|
||||||
func wellKnownTypeUnmarshaler(name protoreflect.FullName) unmarshalFunc {
|
func wellKnownTypeUnmarshaler(name pref.FullName) unmarshalFunc {
|
||||||
if name.Parent() == genid.GoogleProtobuf_package {
|
if name.Parent() == genid.GoogleProtobuf_package {
|
||||||
switch name.Name() {
|
switch name.Name() {
|
||||||
case genid.Any_message_name:
|
case genid.Any_message_name:
|
||||||
@ -843,6 +842,9 @@ func (e encoder) marshalFieldMask(m pref.Message) error {
|
|||||||
|
|
||||||
for i := 0; i < list.Len(); i++ {
|
for i := 0; i < list.Len(); i++ {
|
||||||
s := list.Get(i).String()
|
s := list.Get(i).String()
|
||||||
|
if !pref.FullName(s).IsValid() {
|
||||||
|
return errors.New("%s contains invalid path: %q", genid.FieldMask_Paths_field_fullname, s)
|
||||||
|
}
|
||||||
// Return error if conversion to camelCase is not reversible.
|
// Return error if conversion to camelCase is not reversible.
|
||||||
cc := strs.JSONCamelCase(s)
|
cc := strs.JSONCamelCase(s)
|
||||||
if s != strs.JSONSnakeCase(cc) {
|
if s != strs.JSONSnakeCase(cc) {
|
||||||
@ -872,11 +874,12 @@ func (d decoder) unmarshalFieldMask(m pref.Message) error {
|
|||||||
fd := m.Descriptor().Fields().ByNumber(genid.FieldMask_Paths_field_number)
|
fd := m.Descriptor().Fields().ByNumber(genid.FieldMask_Paths_field_number)
|
||||||
list := m.Mutable(fd).List()
|
list := m.Mutable(fd).List()
|
||||||
|
|
||||||
for _, s := range paths {
|
for _, s0 := range paths {
|
||||||
s = strings.TrimSpace(s)
|
s := strs.JSONSnakeCase(s0)
|
||||||
// Convert to snake_case. Unlike encoding, no validation is done because
|
if strings.Contains(s0, "_") || !pref.FullName(s).IsValid() {
|
||||||
// it is not possible to know the original path names.
|
return d.newError(tok.Pos(), "%v contains invalid path: %q", genid.FieldMask_Paths_field_fullname, s0)
|
||||||
list.Append(pref.ValueOfString(strs.JSONSnakeCase(s)))
|
}
|
||||||
|
list.Append(pref.ValueOfString(s))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter
|
|
Loading…
x
Reference in New Issue
Block a user