desc_validate.go raw

   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  		}
 120  
 121  		for j, fd := range md.GetField() {
 122  			f := &m.L2.Fields.List[j]
 123  			if m.L2.ReservedNames.Has(f.Name()) {
 124  				return errors.New("message field %q must not use reserved name", f.FullName())
 125  			}
 126  			if !f.Number().IsValid() {
 127  				return errors.New("message field %q has an invalid number: %d", f.FullName(), f.Number())
 128  			}
 129  			if !f.Cardinality().IsValid() {
 130  				return errors.New("message field %q has an invalid cardinality: %d", f.FullName(), f.Cardinality())
 131  			}
 132  			if m.L2.ReservedRanges.Has(f.Number()) {
 133  				return errors.New("message field %q must not use reserved number %d", f.FullName(), f.Number())
 134  			}
 135  			if m.L2.ExtensionRanges.Has(f.Number()) {
 136  				return errors.New("message field %q with number %d in extension range", f.FullName(), f.Number())
 137  			}
 138  			if fd.Extendee != nil {
 139  				return errors.New("message field %q may not have extendee: %q", f.FullName(), fd.GetExtendee())
 140  			}
 141  			if f.L1.IsProto3Optional {
 142  				if !isProto3 {
 143  					return errors.New("message field %q under proto3 optional semantics must be specified in the proto3 syntax", f.FullName())
 144  				}
 145  				if f.Cardinality() != protoreflect.Optional {
 146  					return errors.New("message field %q under proto3 optional semantics must have optional cardinality", f.FullName())
 147  				}
 148  				if f.ContainingOneof() != nil && f.ContainingOneof().Fields().Len() != 1 {
 149  					return errors.New("message field %q under proto3 optional semantics must be within a single element oneof", f.FullName())
 150  				}
 151  			}
 152  			if f.IsPacked() && !isPackable(f) {
 153  				return errors.New("message field %q is not packable", f.FullName())
 154  			}
 155  			if err := checkValidGroup(file, f); err != nil {
 156  				return errors.New("message field %q is an invalid group: %v", f.FullName(), err)
 157  			}
 158  			if err := checkValidMap(f); err != nil {
 159  				return errors.New("message field %q is an invalid map: %v", f.FullName(), err)
 160  			}
 161  			if isProto3 {
 162  				if f.Cardinality() == protoreflect.Required {
 163  					return errors.New("message field %q using proto3 semantics cannot be required", f.FullName())
 164  				}
 165  				if f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().IsClosed() {
 166  					return errors.New("message field %q using proto3 semantics may only depend on open enums", f.FullName())
 167  				}
 168  			}
 169  			if f.Cardinality() == protoreflect.Optional && !f.HasPresence() && f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().IsClosed() {
 170  				return errors.New("message field %q with implicit presence may only use open enums", f.FullName())
 171  			}
 172  		}
 173  		seenSynthetic := false // synthetic oneofs for proto3 optional must come after real oneofs
 174  		for j := range md.GetOneofDecl() {
 175  			o := &m.L2.Oneofs.List[j]
 176  			if o.Fields().Len() == 0 {
 177  				return errors.New("message oneof %q must contain at least one field declaration", o.FullName())
 178  			}
 179  			if n := o.Fields().Len(); n-1 != (o.Fields().Get(n-1).Index() - o.Fields().Get(0).Index()) {
 180  				return errors.New("message oneof %q must have consecutively declared fields", o.FullName())
 181  			}
 182  
 183  			if o.IsSynthetic() {
 184  				seenSynthetic = true
 185  				continue
 186  			}
 187  			if !o.IsSynthetic() && seenSynthetic {
 188  				return errors.New("message oneof %q must be declared before synthetic oneofs", o.FullName())
 189  			}
 190  
 191  			for i := 0; i < o.Fields().Len(); i++ {
 192  				f := o.Fields().Get(i)
 193  				if f.Cardinality() != protoreflect.Optional {
 194  					return errors.New("message field %q belongs in a oneof and must be optional", f.FullName())
 195  				}
 196  			}
 197  		}
 198  
 199  		if err := validateEnumDeclarations(m.L1.Enums.List, md.GetEnumType()); err != nil {
 200  			return err
 201  		}
 202  		if err := validateMessageDeclarations(file, m.L1.Messages.List, md.GetNestedType()); err != nil {
 203  			return err
 204  		}
 205  		if err := validateExtensionDeclarations(file, m.L1.Extensions.List, md.GetExtension()); err != nil {
 206  			return err
 207  		}
 208  	}
 209  	return nil
 210  }
 211  
 212  func validateExtensionDeclarations(f *filedesc.File, xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto) error {
 213  	for i, xd := range xds {
 214  		x := &xs[i]
 215  		// NOTE: Avoid using the IsValid method since extensions to MessageSet
 216  		// may have a field number higher than normal. This check only verifies
 217  		// that the number is not negative or reserved. We check again later
 218  		// if we know that the extendee is definitely not a MessageSet.
 219  		if n := x.Number(); n < 0 || (protowire.FirstReservedNumber <= n && n <= protowire.LastReservedNumber) {
 220  			return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number())
 221  		}
 222  		if !x.Cardinality().IsValid() || x.Cardinality() == protoreflect.Required {
 223  			return errors.New("extension field %q has an invalid cardinality: %d", x.FullName(), x.Cardinality())
 224  		}
 225  		if xd.JsonName != nil {
 226  			// A bug in older versions of protoc would always populate the
 227  			// "json_name" option for extensions when it is meaningless.
 228  			// When it did so, it would always use the camel-cased field name.
 229  			if xd.GetJsonName() != strs.JSONCamelCase(string(x.Name())) {
 230  				return errors.New("extension field %q may not have an explicitly set JSON name: %q", x.FullName(), xd.GetJsonName())
 231  			}
 232  		}
 233  		if xd.OneofIndex != nil {
 234  			return errors.New("extension field %q may not be part of a oneof", x.FullName())
 235  		}
 236  		if md := x.ContainingMessage(); !md.IsPlaceholder() {
 237  			if !md.ExtensionRanges().Has(x.Number()) {
 238  				return errors.New("extension field %q extends %q with non-extension field number: %d", x.FullName(), md.FullName(), x.Number())
 239  			}
 240  			isMessageSet := md.Options().(*descriptorpb.MessageOptions).GetMessageSetWireFormat()
 241  			if isMessageSet && !isOptionalMessage(x) {
 242  				return errors.New("extension field %q extends MessageSet and must be an optional message", x.FullName())
 243  			}
 244  			if !isMessageSet && !x.Number().IsValid() {
 245  				return errors.New("extension field %q has an invalid number: %d", x.FullName(), x.Number())
 246  			}
 247  		}
 248  		if x.IsPacked() && !isPackable(x) {
 249  			return errors.New("extension field %q is not packable", x.FullName())
 250  		}
 251  		if err := checkValidGroup(f, x); err != nil {
 252  			return errors.New("extension field %q is an invalid group: %v", x.FullName(), err)
 253  		}
 254  		if md := x.Message(); md != nil && md.IsMapEntry() {
 255  			return errors.New("extension field %q cannot be a map entry", x.FullName())
 256  		}
 257  		if f.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3) {
 258  			switch x.ContainingMessage().FullName() {
 259  			case (*descriptorpb.FileOptions)(nil).ProtoReflect().Descriptor().FullName():
 260  			case (*descriptorpb.EnumOptions)(nil).ProtoReflect().Descriptor().FullName():
 261  			case (*descriptorpb.EnumValueOptions)(nil).ProtoReflect().Descriptor().FullName():
 262  			case (*descriptorpb.MessageOptions)(nil).ProtoReflect().Descriptor().FullName():
 263  			case (*descriptorpb.FieldOptions)(nil).ProtoReflect().Descriptor().FullName():
 264  			case (*descriptorpb.OneofOptions)(nil).ProtoReflect().Descriptor().FullName():
 265  			case (*descriptorpb.ExtensionRangeOptions)(nil).ProtoReflect().Descriptor().FullName():
 266  			case (*descriptorpb.ServiceOptions)(nil).ProtoReflect().Descriptor().FullName():
 267  			case (*descriptorpb.MethodOptions)(nil).ProtoReflect().Descriptor().FullName():
 268  			default:
 269  				return errors.New("extension field %q cannot be declared in proto3 unless extended descriptor options", x.FullName())
 270  			}
 271  		}
 272  	}
 273  	return nil
 274  }
 275  
 276  // isOptionalMessage reports whether this is an optional message.
 277  // If the kind is unknown, it is assumed to be a message.
 278  func isOptionalMessage(fd protoreflect.FieldDescriptor) bool {
 279  	return (fd.Kind() == 0 || fd.Kind() == protoreflect.MessageKind) && fd.Cardinality() == protoreflect.Optional
 280  }
 281  
 282  // isPackable checks whether the pack option can be specified.
 283  func isPackable(fd protoreflect.FieldDescriptor) bool {
 284  	switch fd.Kind() {
 285  	case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind:
 286  		return false
 287  	}
 288  	return fd.IsList()
 289  }
 290  
 291  // checkValidGroup reports whether fd is a valid group according to the same
 292  // rules that protoc imposes.
 293  func checkValidGroup(f *filedesc.File, fd protoreflect.FieldDescriptor) error {
 294  	md := fd.Message()
 295  	switch {
 296  	case fd.Kind() != protoreflect.GroupKind:
 297  		return nil
 298  	case f.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3):
 299  		return errors.New("invalid under proto3 semantics")
 300  	case md == nil || md.IsPlaceholder():
 301  		return errors.New("message must be resolvable")
 302  	}
 303  	if f.L1.Edition < fromEditionProto(descriptorpb.Edition_EDITION_2023) {
 304  		switch {
 305  		case fd.FullName().Parent() != md.FullName().Parent():
 306  			return errors.New("message and field must be declared in the same scope")
 307  		case !unicode.IsUpper(rune(md.Name()[0])):
 308  			return errors.New("message name must start with an uppercase")
 309  		case fd.Name() != protoreflect.Name(strings.ToLower(string(md.Name()))):
 310  			return errors.New("field name must be lowercased form of the message name")
 311  		}
 312  	}
 313  	return nil
 314  }
 315  
 316  // checkValidMap checks whether the field is a valid map according to the same
 317  // rules that protoc imposes.
 318  // See protoc v3.8.0: src/google/protobuf/descriptor.cc:6045-6115
 319  func checkValidMap(fd protoreflect.FieldDescriptor) error {
 320  	md := fd.Message()
 321  	switch {
 322  	case md == nil || !md.IsMapEntry():
 323  		return nil
 324  	case fd.FullName().Parent() != md.FullName().Parent():
 325  		return errors.New("message and field must be declared in the same scope")
 326  	case md.Name() != protoreflect.Name(strs.MapEntryName(string(fd.Name()))):
 327  		return errors.New("incorrect implicit map entry name")
 328  	case fd.Cardinality() != protoreflect.Repeated:
 329  		return errors.New("field must be repeated")
 330  	case md.Fields().Len() != 2:
 331  		return errors.New("message must have exactly two fields")
 332  	case md.ExtensionRanges().Len() > 0:
 333  		return errors.New("message must not have any extension ranges")
 334  	case md.Enums().Len()+md.Messages().Len()+md.Extensions().Len() > 0:
 335  		return errors.New("message must not have any nested declarations")
 336  	}
 337  	kf := md.Fields().Get(0)
 338  	vf := md.Fields().Get(1)
 339  	switch {
 340  	case kf.Name() != genid.MapEntry_Key_field_name || kf.Number() != genid.MapEntry_Key_field_number || kf.Cardinality() != protoreflect.Optional || kf.ContainingOneof() != nil || kf.HasDefault():
 341  		return errors.New("invalid key field")
 342  	case vf.Name() != genid.MapEntry_Value_field_name || vf.Number() != genid.MapEntry_Value_field_number || vf.Cardinality() != protoreflect.Optional || vf.ContainingOneof() != nil || vf.HasDefault():
 343  		return errors.New("invalid value field")
 344  	}
 345  	switch kf.Kind() {
 346  	case protoreflect.BoolKind: // bool
 347  	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: // int32
 348  	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: // int64
 349  	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: // uint32
 350  	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: // uint64
 351  	case protoreflect.StringKind: // string
 352  	default:
 353  		return errors.New("invalid key kind: %v", kf.Kind())
 354  	}
 355  	if e := vf.Enum(); e != nil && e.Values().Len() > 0 && e.Values().Get(0).Number() != 0 {
 356  		return errors.New("map enum value must have zero number for the first value")
 357  	}
 358  	return nil
 359  }
 360