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