...

Source file src/google.golang.org/protobuf/reflect/protodesc/desc_validate.go

Documentation: google.golang.org/protobuf/reflect/protodesc

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package protodesc
     6  
     7  import (
     8  	"strings"
     9  	"unicode"
    10  
    11  	"google.golang.org/protobuf/encoding/protowire"
    12  	"google.golang.org/protobuf/internal/errors"
    13  	"google.golang.org/protobuf/internal/filedesc"
    14  	"google.golang.org/protobuf/internal/flags"
    15  	"google.golang.org/protobuf/internal/genid"
    16  	"google.golang.org/protobuf/internal/strs"
    17  	"google.golang.org/protobuf/reflect/protoreflect"
    18  
    19  	"google.golang.org/protobuf/types/descriptorpb"
    20  )
    21  
    22  func validateEnumDeclarations(es []filedesc.Enum, eds []*descriptorpb.EnumDescriptorProto) error {
    23  	for i, ed := range eds {
    24  		e := &es[i]
    25  		if err := e.L2.ReservedNames.CheckValid(); err != nil {
    26  			return errors.New("enum %q reserved names has %v", e.FullName(), err)
    27  		}
    28  		if err := e.L2.ReservedRanges.CheckValid(); err != nil {
    29  			return errors.New("enum %q reserved ranges has %v", e.FullName(), err)
    30  		}
    31  		if len(ed.GetValue()) == 0 {
    32  			return errors.New("enum %q must contain at least one value declaration", e.FullName())
    33  		}
    34  		allowAlias := ed.GetOptions().GetAllowAlias()
    35  		foundAlias := false
    36  		for i := 0; i < e.Values().Len(); i++ {
    37  			v1 := e.Values().Get(i)
    38  			if v2 := e.Values().ByNumber(v1.Number()); v1 != v2 {
    39  				foundAlias = true
    40  				if !allowAlias {
    41  					return errors.New("enum %q has conflicting non-aliased values on number %d: %q with %q", e.FullName(), v1.Number(), v1.Name(), v2.Name())
    42  				}
    43  			}
    44  		}
    45  		if allowAlias && !foundAlias {
    46  			return errors.New("enum %q allows aliases, but none were found", e.FullName())
    47  		}
    48  		if !e.IsClosed() {
    49  			if v := e.Values().Get(0); v.Number() != 0 {
    50  				return errors.New("enum %q using open semantics must have zero number for the first value", v.FullName())
    51  			}
    52  			// Verify that value names in open enums do not conflict if the
    53  			// case-insensitive prefix is removed.
    54  			// See protoc v3.8.0: src/google/protobuf/descriptor.cc:4991-5055
    55  			names := map[string]protoreflect.EnumValueDescriptor{}
    56  			prefix := strings.Replace(strings.ToLower(string(e.Name())), "_", "", -1)
    57  			for i := 0; i < e.Values().Len(); i++ {
    58  				v1 := e.Values().Get(i)
    59  				s := strs.EnumValueName(strs.TrimEnumPrefix(string(v1.Name()), prefix))
    60  				if v2, ok := names[s]; ok && v1.Number() != v2.Number() {
    61  					return errors.New("enum %q using open semantics has conflict: %q with %q", e.FullName(), v1.Name(), v2.Name())
    62  				}
    63  				names[s] = v1
    64  			}
    65  		}
    66  
    67  		for j, vd := range ed.GetValue() {
    68  			v := &e.L2.Values.List[j]
    69  			if vd.Number == nil {
    70  				return errors.New("enum value %q must have a specified number", v.FullName())
    71  			}
    72  			if e.L2.ReservedNames.Has(v.Name()) {
    73  				return errors.New("enum value %q must not use reserved name", v.FullName())
    74  			}
    75  			if e.L2.ReservedRanges.Has(v.Number()) {
    76  				return errors.New("enum value %q must not use reserved number %d", v.FullName(), v.Number())
    77  			}
    78  		}
    79  	}
    80  	return nil
    81  }
    82  
    83  func validateMessageDeclarations(file *filedesc.File, ms []filedesc.Message, mds []*descriptorpb.DescriptorProto) error {
    84  	// There are a few limited exceptions only for proto3
    85  	isProto3 := file.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3)
    86  	for i, md := range mds {
    87  		m := &ms[i]
    88  
    89  		// Handle the message descriptor itself.
    90  		isMessageSet := md.GetOptions().GetMessageSetWireFormat()
    91  		if err := m.L2.ReservedNames.CheckValid(); err != nil {
    92  			return errors.New("message %q reserved names has %v", m.FullName(), err)
    93  		}
    94  		if err := m.L2.ReservedRanges.CheckValid(isMessageSet); err != nil {
    95  			return errors.New("message %q reserved ranges has %v", m.FullName(), err)
    96  		}
    97  		if err := m.L2.ExtensionRanges.CheckValid(isMessageSet); err != nil {
    98  			return errors.New("message %q extension ranges has %v", m.FullName(), err)
    99  		}
   100  		if err := (*filedesc.FieldRanges).CheckOverlap(&m.L2.ReservedRanges, &m.L2.ExtensionRanges); err != nil {
   101  			return errors.New("message %q reserved and extension ranges has %v", m.FullName(), err)
   102  		}
   103  		for i := 0; i < m.Fields().Len(); i++ {
   104  			f1 := m.Fields().Get(i)
   105  			if f2 := m.Fields().ByNumber(f1.Number()); f1 != f2 {
   106  				return errors.New("message %q has conflicting fields: %q with %q", m.FullName(), f1.Name(), f2.Name())
   107  			}
   108  		}
   109  		if isMessageSet && !flags.ProtoLegacy {
   110  			return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName())
   111  		}
   112  		if isMessageSet && (isProto3 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) {
   113  			return errors.New("message %q is an invalid proto1 MessageSet", m.FullName())
   114  		}
   115  		if isProto3 {
   116  			if m.ExtensionRanges().Len() > 0 {
   117  				return errors.New("message %q using proto3 semantics cannot have extension ranges", m.FullName())
   118  			}
   119  			// Verify that field names in proto3 do not conflict if lowercased
   120  			// with all underscores removed.
   121  			// See protoc v3.8.0: src/google/protobuf/descriptor.cc:5830-5847
   122  			names := map[string]protoreflect.FieldDescriptor{}
   123  			for i := 0; i < m.Fields().Len(); i++ {
   124  				f1 := m.Fields().Get(i)
   125  				s := strings.Replace(strings.ToLower(string(f1.Name())), "_", "", -1)
   126  				if f2, ok := names[s]; ok {
   127  					return errors.New("message %q using proto3 semantics has conflict: %q with %q", m.FullName(), f1.Name(), f2.Name())
   128  				}
   129  				names[s] = f1
   130  			}
   131  		}
   132  
   133  		for j, fd := range md.GetField() {
   134  			f := &m.L2.Fields.List[j]
   135  			if m.L2.ReservedNames.Has(f.Name()) {
   136  				return errors.New("message field %q must not use reserved name", f.FullName())
   137  			}
   138  			if !f.Number().IsValid() {
   139  				return errors.New("message field %q has an invalid number: %d", f.FullName(), f.Number())
   140  			}
   141  			if !f.Cardinality().IsValid() {
   142  				return errors.New("message field %q has an invalid cardinality: %d", f.FullName(), f.Cardinality())
   143  			}
   144  			if m.L2.ReservedRanges.Has(f.Number()) {
   145  				return errors.New("message field %q must not use reserved number %d", f.FullName(), f.Number())
   146  			}
   147  			if m.L2.ExtensionRanges.Has(f.Number()) {
   148  				return errors.New("message field %q with number %d in extension range", f.FullName(), f.Number())
   149  			}
   150  			if fd.Extendee != nil {
   151  				return errors.New("message field %q may not have extendee: %q", f.FullName(), fd.GetExtendee())
   152  			}
   153  			if f.L1.IsProto3Optional {
   154  				if !isProto3 {
   155  					return errors.New("message field %q under proto3 optional semantics must be specified in the proto3 syntax", f.FullName())
   156  				}
   157  				if f.Cardinality() != protoreflect.Optional {
   158  					return errors.New("message field %q under proto3 optional semantics must have optional cardinality", f.FullName())
   159  				}
   160  				if f.ContainingOneof() != nil && f.ContainingOneof().Fields().Len() != 1 {
   161  					return errors.New("message field %q under proto3 optional semantics must be within a single element oneof", f.FullName())
   162  				}
   163  			}
   164  			if f.IsWeak() && !flags.ProtoLegacy {
   165  				return errors.New("message field %q is a weak field, which is a legacy proto1 feature that is no longer supported", f.FullName())
   166  			}
   167  			if f.IsWeak() && (!f.HasPresence() || !isOptionalMessage(f) || f.ContainingOneof() != nil) {
   168  				return errors.New("message field %q may only be weak for an optional message", f.FullName())
   169  			}
   170  			if f.IsPacked() && !isPackable(f) {
   171  				return errors.New("message field %q is not packable", f.FullName())
   172  			}
   173  			if err := checkValidGroup(file, f); err != nil {
   174  				return errors.New("message field %q is an invalid group: %v", f.FullName(), err)
   175  			}
   176  			if err := checkValidMap(f); err != nil {
   177  				return errors.New("message field %q is an invalid map: %v", f.FullName(), err)
   178  			}
   179  			if isProto3 {
   180  				if f.Cardinality() == protoreflect.Required {
   181  					return errors.New("message field %q using proto3 semantics cannot be required", f.FullName())
   182  				}
   183  				if f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().IsClosed() {
   184  					return errors.New("message field %q using proto3 semantics may only depend on open enums", f.FullName())
   185  				}
   186  			}
   187  			if f.Cardinality() == protoreflect.Optional && !f.HasPresence() && f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().IsClosed() {
   188  				return errors.New("message field %q with implicit presence may only use open enums", f.FullName())
   189  			}
   190  		}
   191  		seenSynthetic := false // synthetic oneofs for proto3 optional must come after real oneofs
   192  		for j := range md.GetOneofDecl() {
   193  			o := &m.L2.Oneofs.List[j]
   194  			if o.Fields().Len() == 0 {
   195  				return errors.New("message oneof %q must contain at least one field declaration", o.FullName())
   196  			}
   197  			if n := o.Fields().Len(); n-1 != (o.Fields().Get(n-1).Index() - o.Fields().Get(0).Index()) {
   198  				return errors.New("message oneof %q must have consecutively declared fields", o.FullName())
   199  			}
   200  
   201  			if o.IsSynthetic() {
   202  				seenSynthetic = true
   203  				continue
   204  			}
   205  			if !o.IsSynthetic() && seenSynthetic {
   206  				return errors.New("message oneof %q must be declared before synthetic oneofs", o.FullName())
   207  			}
   208  
   209  			for i := 0; i < o.Fields().Len(); i++ {
   210  				f := o.Fields().Get(i)
   211  				if f.Cardinality() != protoreflect.Optional {
   212  					return errors.New("message field %q belongs in a oneof and must be optional", f.FullName())
   213  				}
   214  				if f.IsWeak() {
   215  					return errors.New("message field %q belongs in a oneof and must not be a weak reference", f.FullName())
   216  				}
   217  			}
   218  		}
   219  
   220  		if err := validateEnumDeclarations(m.L1.Enums.List, md.GetEnumType()); err != nil {
   221  			return err
   222  		}
   223  		if err := validateMessageDeclarations(file, m.L1.Messages.List, md.GetNestedType()); err != nil {
   224  			return err
   225  		}
   226  		if err := validateExtensionDeclarations(file, m.L1.Extensions.List, md.GetExtension()); err != nil {
   227  			return err
   228  		}
   229  	}
   230  	return nil
   231  }
   232  
   233  func validateExtensionDeclarations(f *filedesc.File, xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto) error {
   234  	for i, xd := range xds {
   235  		x := &xs[i]
   236  		// NOTE: Avoid using the IsValid method since extensions to MessageSet
   237  		// may have a field number higher than normal. This check only verifies
   238  		// that the number is not negative or reserved. We check again later
   239  		// if we know that the extendee is definitely not a MessageSet.
   240  		if n := x.Number(); n < 0 || (protowire.FirstReservedNumber <= n && n <= protowire.LastReservedNumber) {
   241  			return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number())
   242  		}
   243  		if !x.Cardinality().IsValid() || x.Cardinality() == protoreflect.Required {
   244  			return errors.New("extension field %q has an invalid cardinality: %d", x.FullName(), x.Cardinality())
   245  		}
   246  		if xd.JsonName != nil {
   247  			// A bug in older versions of protoc would always populate the
   248  			// "json_name" option for extensions when it is meaningless.
   249  			// When it did so, it would always use the camel-cased field name.
   250  			if xd.GetJsonName() != strs.JSONCamelCase(string(x.Name())) {
   251  				return errors.New("extension field %q may not have an explicitly set JSON name: %q", x.FullName(), xd.GetJsonName())
   252  			}
   253  		}
   254  		if xd.OneofIndex != nil {
   255  			return errors.New("extension field %q may not be part of a oneof", x.FullName())
   256  		}
   257  		if md := x.ContainingMessage(); !md.IsPlaceholder() {
   258  			if !md.ExtensionRanges().Has(x.Number()) {
   259  				return errors.New("extension field %q extends %q with non-extension field number: %d", x.FullName(), md.FullName(), x.Number())
   260  			}
   261  			isMessageSet := md.Options().(*descriptorpb.MessageOptions).GetMessageSetWireFormat()
   262  			if isMessageSet && !isOptionalMessage(x) {
   263  				return errors.New("extension field %q extends MessageSet and must be an optional message", x.FullName())
   264  			}
   265  			if !isMessageSet && !x.Number().IsValid() {
   266  				return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number())
   267  			}
   268  		}
   269  		if xd.GetOptions().GetWeak() {
   270  			return errors.New("extension field %q cannot be a weak reference", x.FullName())
   271  		}
   272  		if x.IsPacked() && !isPackable(x) {
   273  			return errors.New("extension field %q is not packable", x.FullName())
   274  		}
   275  		if err := checkValidGroup(f, x); err != nil {
   276  			return errors.New("extension field %q is an invalid group: %v", x.FullName(), err)
   277  		}
   278  		if md := x.Message(); md != nil && md.IsMapEntry() {
   279  			return errors.New("extension field %q cannot be a map entry", x.FullName())
   280  		}
   281  		if f.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3) {
   282  			switch x.ContainingMessage().FullName() {
   283  			case (*descriptorpb.FileOptions)(nil).ProtoReflect().Descriptor().FullName():
   284  			case (*descriptorpb.EnumOptions)(nil).ProtoReflect().Descriptor().FullName():
   285  			case (*descriptorpb.EnumValueOptions)(nil).ProtoReflect().Descriptor().FullName():
   286  			case (*descriptorpb.MessageOptions)(nil).ProtoReflect().Descriptor().FullName():
   287  			case (*descriptorpb.FieldOptions)(nil).ProtoReflect().Descriptor().FullName():
   288  			case (*descriptorpb.OneofOptions)(nil).ProtoReflect().Descriptor().FullName():
   289  			case (*descriptorpb.ExtensionRangeOptions)(nil).ProtoReflect().Descriptor().FullName():
   290  			case (*descriptorpb.ServiceOptions)(nil).ProtoReflect().Descriptor().FullName():
   291  			case (*descriptorpb.MethodOptions)(nil).ProtoReflect().Descriptor().FullName():
   292  			default:
   293  				return errors.New("extension field %q cannot be declared in proto3 unless extended descriptor options", x.FullName())
   294  			}
   295  		}
   296  	}
   297  	return nil
   298  }
   299  
   300  // isOptionalMessage reports whether this is an optional message.
   301  // If the kind is unknown, it is assumed to be a message.
   302  func isOptionalMessage(fd protoreflect.FieldDescriptor) bool {
   303  	return (fd.Kind() == 0 || fd.Kind() == protoreflect.MessageKind) && fd.Cardinality() == protoreflect.Optional
   304  }
   305  
   306  // isPackable checks whether the pack option can be specified.
   307  func isPackable(fd protoreflect.FieldDescriptor) bool {
   308  	switch fd.Kind() {
   309  	case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind:
   310  		return false
   311  	}
   312  	return fd.IsList()
   313  }
   314  
   315  // checkValidGroup reports whether fd is a valid group according to the same
   316  // rules that protoc imposes.
   317  func checkValidGroup(f *filedesc.File, fd protoreflect.FieldDescriptor) error {
   318  	md := fd.Message()
   319  	switch {
   320  	case fd.Kind() != protoreflect.GroupKind:
   321  		return nil
   322  	case f.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3):
   323  		return errors.New("invalid under proto3 semantics")
   324  	case md == nil || md.IsPlaceholder():
   325  		return errors.New("message must be resolvable")
   326  	}
   327  	if f.L1.Edition < fromEditionProto(descriptorpb.Edition_EDITION_2023) {
   328  		switch {
   329  		case fd.FullName().Parent() != md.FullName().Parent():
   330  			return errors.New("message and field must be declared in the same scope")
   331  		case !unicode.IsUpper(rune(md.Name()[0])):
   332  			return errors.New("message name must start with an uppercase")
   333  		case fd.Name() != protoreflect.Name(strings.ToLower(string(md.Name()))):
   334  			return errors.New("field name must be lowercased form of the message name")
   335  		}
   336  	}
   337  	return nil
   338  }
   339  
   340  // checkValidMap checks whether the field is a valid map according to the same
   341  // rules that protoc imposes.
   342  // See protoc v3.8.0: src/google/protobuf/descriptor.cc:6045-6115
   343  func checkValidMap(fd protoreflect.FieldDescriptor) error {
   344  	md := fd.Message()
   345  	switch {
   346  	case md == nil || !md.IsMapEntry():
   347  		return nil
   348  	case fd.FullName().Parent() != md.FullName().Parent():
   349  		return errors.New("message and field must be declared in the same scope")
   350  	case md.Name() != protoreflect.Name(strs.MapEntryName(string(fd.Name()))):
   351  		return errors.New("incorrect implicit map entry name")
   352  	case fd.Cardinality() != protoreflect.Repeated:
   353  		return errors.New("field must be repeated")
   354  	case md.Fields().Len() != 2:
   355  		return errors.New("message must have exactly two fields")
   356  	case md.ExtensionRanges().Len() > 0:
   357  		return errors.New("message must not have any extension ranges")
   358  	case md.Enums().Len()+md.Messages().Len()+md.Extensions().Len() > 0:
   359  		return errors.New("message must not have any nested declarations")
   360  	}
   361  	kf := md.Fields().Get(0)
   362  	vf := md.Fields().Get(1)
   363  	switch {
   364  	case kf.Name() != genid.MapEntry_Key_field_name || kf.Number() != genid.MapEntry_Key_field_number || kf.Cardinality() != protoreflect.Optional || kf.ContainingOneof() != nil || kf.HasDefault():
   365  		return errors.New("invalid key field")
   366  	case vf.Name() != genid.MapEntry_Value_field_name || vf.Number() != genid.MapEntry_Value_field_number || vf.Cardinality() != protoreflect.Optional || vf.ContainingOneof() != nil || vf.HasDefault():
   367  		return errors.New("invalid value field")
   368  	}
   369  	switch kf.Kind() {
   370  	case protoreflect.BoolKind: // bool
   371  	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: // int32
   372  	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: // int64
   373  	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: // uint32
   374  	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: // uint64
   375  	case protoreflect.StringKind: // string
   376  	default:
   377  		return errors.New("invalid key kind: %v", kf.Kind())
   378  	}
   379  	if e := vf.Enum(); e != nil && e.Values().Len() > 0 && e.Values().Get(0).Number() != 0 {
   380  		return errors.New("map enum value must have zero number for the first value")
   381  	}
   382  	return nil
   383  }
   384  

View as plain text