From 7614117db689b52cef526d3a25f97b53eaa8199f Mon Sep 17 00:00:00 2001 From: Joshua Humphries Date: Mon, 15 Aug 2022 16:39:39 -0400 Subject: [PATCH] protoparse: it is not allowed to reference a synthetic map entry (#522) --- desc/protoparse/linker.go | 19 +++++++++++++++++++ desc/protoparse/linker_test.go | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/desc/protoparse/linker.go b/desc/protoparse/linker.go index 1fea1c95..d80ebdee 100644 --- a/desc/protoparse/linker.go +++ b/desc/protoparse/linker.go @@ -3,6 +3,7 @@ package protoparse import ( "bytes" "fmt" + "google.golang.org/protobuf/types/descriptorpb" "sort" "strings" @@ -518,6 +519,24 @@ func (l *linker) resolveFieldTypes(r *parseResult, fd *dpb.FileDescriptorProto, } switch dsc := dsc.(type) { case *dpb.DescriptorProto: + if dsc.GetOptions().GetMapEntry() { + isValid := false + switch node.(type) { + case *ast.MapFieldNode: + // We have an AST for this file and can see this field is from a map declaration + isValid = true + case ast.NoSourceNode: + // We don't have an AST for the file (it came from a provided descriptor). So we + // need to validate that it's not an illegal reference. To be valid, the field + // must be repeated and the entry type must be nested in the same enclosing + // message as the field. + expectFqn := prefix + dsc.GetName() + isValid = expectFqn == fqn && fld.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED + } + if !isValid { + return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: %s is a synthetic map entry and may not be referenced explicitly", scope, fqn) + } + } fld.TypeName = proto.String("." + fqn) // if type was tentatively unset, we now know it's actually a message if fld.Type == nil { diff --git a/desc/protoparse/linker_test.go b/desc/protoparse/linker_test.go index 9b3935b4..00241195 100644 --- a/desc/protoparse/linker_test.go +++ b/desc/protoparse/linker_test.go @@ -984,6 +984,28 @@ func TestLinkerValidation(t *testing.T) { }, "", // should succeed }, + { + map[string]string{ + "foo.proto": "syntax = \"proto3\";\n" + + "message Foo {\n" + + " map bar = 1;\n" + + "}\n" + + "message Baz {\n" + + " Foo.BarEntry e = 1;\n" + + "}\n", + }, + "foo.proto:6:3: field Baz.e: Foo.BarEntry is a synthetic map entry and may not be referenced explicitly", + }, + { + map[string]string{ + "foo.proto": "syntax = \"proto3\";\n" + + "import \"google/protobuf/struct.proto\";\n" + + "message Foo {\n" + + " google.protobuf.Struct.FieldsEntry e = 1;\n" + + "}\n", + }, + "foo.proto:4:3: field Foo.e: google.protobuf.Struct.FieldsEntry is a synthetic map entry and may not be referenced explicitly", + }, } for i, tc := range testCases { acc := func(filename string) (io.ReadCloser, error) {