.examples
.github
alerting
client
config
controller
core
docs
jsonpath
metrics
pattern
security
storage
test
util
vendor
github.com
golang.org
google.golang.org
appengine
protobuf
encoding
internal
descfmt
descopts
detrand
encoding
errors
filedesc
filetype
flags
genid
impl
api_export.go
checkinit.go
codec_extension.go
codec_field.go
codec_gen.go
codec_map.go
codec_map_go111.go
codec_map_go112.go
codec_message.go
codec_messageset.go
codec_reflect.go
codec_tables.go
codec_unsafe.go
convert.go
convert_list.go
convert_map.go
decode.go
encode.go
enum.go
extension.go
legacy_enum.go
legacy_export.go
legacy_extension.go
legacy_file.go
legacy_message.go
merge.go
merge_gen.go
message.go
message_reflect.go
message_reflect_field.go
message_reflect_gen.go
pointer_reflect.go
pointer_unsafe.go
validate.go
weak.go
order
pragma
set
strs
version
proto
reflect
runtime
types
LICENSE
PATENTS
gopkg.in
lukechampine.com
modernc.org
modules.txt
watchdog
web
.dockerignore
.gitattributes
.gitignore
Dockerfile
LICENSE
Makefile
README.md
config.yaml
go.mod
go.sum
main.go
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.11.0 to 1.13.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.11.0...v1.13.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
577 lines
15 KiB
Go
577 lines
15 KiB
Go
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package impl
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"math/bits"
|
|
"reflect"
|
|
"unicode/utf8"
|
|
|
|
"google.golang.org/protobuf/encoding/protowire"
|
|
"google.golang.org/protobuf/internal/encoding/messageset"
|
|
"google.golang.org/protobuf/internal/flags"
|
|
"google.golang.org/protobuf/internal/genid"
|
|
"google.golang.org/protobuf/internal/strs"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
"google.golang.org/protobuf/reflect/protoregistry"
|
|
"google.golang.org/protobuf/runtime/protoiface"
|
|
)
|
|
|
|
// ValidationStatus is the result of validating the wire-format encoding of a message.
|
|
type ValidationStatus int
|
|
|
|
const (
|
|
// ValidationUnknown indicates that unmarshaling the message might succeed or fail.
|
|
// The validator was unable to render a judgement.
|
|
//
|
|
// The only causes of this status are an aberrant message type appearing somewhere
|
|
// in the message or a failure in the extension resolver.
|
|
ValidationUnknown ValidationStatus = iota + 1
|
|
|
|
// ValidationInvalid indicates that unmarshaling the message will fail.
|
|
ValidationInvalid
|
|
|
|
// ValidationValid indicates that unmarshaling the message will succeed.
|
|
ValidationValid
|
|
)
|
|
|
|
func (v ValidationStatus) String() string {
|
|
switch v {
|
|
case ValidationUnknown:
|
|
return "ValidationUnknown"
|
|
case ValidationInvalid:
|
|
return "ValidationInvalid"
|
|
case ValidationValid:
|
|
return "ValidationValid"
|
|
default:
|
|
return fmt.Sprintf("ValidationStatus(%d)", int(v))
|
|
}
|
|
}
|
|
|
|
// Validate determines whether the contents of the buffer are a valid wire encoding
|
|
// of the message type.
|
|
//
|
|
// This function is exposed for testing.
|
|
func Validate(mt protoreflect.MessageType, in protoiface.UnmarshalInput) (out protoiface.UnmarshalOutput, _ ValidationStatus) {
|
|
mi, ok := mt.(*MessageInfo)
|
|
if !ok {
|
|
return out, ValidationUnknown
|
|
}
|
|
if in.Resolver == nil {
|
|
in.Resolver = protoregistry.GlobalTypes
|
|
}
|
|
o, st := mi.validate(in.Buf, 0, unmarshalOptions{
|
|
flags: in.Flags,
|
|
resolver: in.Resolver,
|
|
})
|
|
if o.initialized {
|
|
out.Flags |= protoiface.UnmarshalInitialized
|
|
}
|
|
return out, st
|
|
}
|
|
|
|
type validationInfo struct {
|
|
mi *MessageInfo
|
|
typ validationType
|
|
keyType, valType validationType
|
|
|
|
// For non-required fields, requiredBit is 0.
|
|
//
|
|
// For required fields, requiredBit's nth bit is set, where n is a
|
|
// unique index in the range [0, MessageInfo.numRequiredFields).
|
|
//
|
|
// If there are more than 64 required fields, requiredBit is 0.
|
|
requiredBit uint64
|
|
}
|
|
|
|
type validationType uint8
|
|
|
|
const (
|
|
validationTypeOther validationType = iota
|
|
validationTypeMessage
|
|
validationTypeGroup
|
|
validationTypeMap
|
|
validationTypeRepeatedVarint
|
|
validationTypeRepeatedFixed32
|
|
validationTypeRepeatedFixed64
|
|
validationTypeVarint
|
|
validationTypeFixed32
|
|
validationTypeFixed64
|
|
validationTypeBytes
|
|
validationTypeUTF8String
|
|
validationTypeMessageSetItem
|
|
)
|
|
|
|
func newFieldValidationInfo(mi *MessageInfo, si structInfo, fd protoreflect.FieldDescriptor, ft reflect.Type) validationInfo {
|
|
var vi validationInfo
|
|
switch {
|
|
case fd.ContainingOneof() != nil && !fd.ContainingOneof().IsSynthetic():
|
|
switch fd.Kind() {
|
|
case protoreflect.MessageKind:
|
|
vi.typ = validationTypeMessage
|
|
if ot, ok := si.oneofWrappersByNumber[fd.Number()]; ok {
|
|
vi.mi = getMessageInfo(ot.Field(0).Type)
|
|
}
|
|
case protoreflect.GroupKind:
|
|
vi.typ = validationTypeGroup
|
|
if ot, ok := si.oneofWrappersByNumber[fd.Number()]; ok {
|
|
vi.mi = getMessageInfo(ot.Field(0).Type)
|
|
}
|
|
case protoreflect.StringKind:
|
|
if strs.EnforceUTF8(fd) {
|
|
vi.typ = validationTypeUTF8String
|
|
}
|
|
}
|
|
default:
|
|
vi = newValidationInfo(fd, ft)
|
|
}
|
|
if fd.Cardinality() == protoreflect.Required {
|
|
// Avoid overflow. The required field check is done with a 64-bit mask, with
|
|
// any message containing more than 64 required fields always reported as
|
|
// potentially uninitialized, so it is not important to get a precise count
|
|
// of the required fields past 64.
|
|
if mi.numRequiredFields < math.MaxUint8 {
|
|
mi.numRequiredFields++
|
|
vi.requiredBit = 1 << (mi.numRequiredFields - 1)
|
|
}
|
|
}
|
|
return vi
|
|
}
|
|
|
|
func newValidationInfo(fd protoreflect.FieldDescriptor, ft reflect.Type) validationInfo {
|
|
var vi validationInfo
|
|
switch {
|
|
case fd.IsList():
|
|
switch fd.Kind() {
|
|
case protoreflect.MessageKind:
|
|
vi.typ = validationTypeMessage
|
|
if ft.Kind() == reflect.Slice {
|
|
vi.mi = getMessageInfo(ft.Elem())
|
|
}
|
|
case protoreflect.GroupKind:
|
|
vi.typ = validationTypeGroup
|
|
if ft.Kind() == reflect.Slice {
|
|
vi.mi = getMessageInfo(ft.Elem())
|
|
}
|
|
case protoreflect.StringKind:
|
|
vi.typ = validationTypeBytes
|
|
if strs.EnforceUTF8(fd) {
|
|
vi.typ = validationTypeUTF8String
|
|
}
|
|
default:
|
|
switch wireTypes[fd.Kind()] {
|
|
case protowire.VarintType:
|
|
vi.typ = validationTypeRepeatedVarint
|
|
case protowire.Fixed32Type:
|
|
vi.typ = validationTypeRepeatedFixed32
|
|
case protowire.Fixed64Type:
|
|
vi.typ = validationTypeRepeatedFixed64
|
|
}
|
|
}
|
|
case fd.IsMap():
|
|
vi.typ = validationTypeMap
|
|
switch fd.MapKey().Kind() {
|
|
case protoreflect.StringKind:
|
|
if strs.EnforceUTF8(fd) {
|
|
vi.keyType = validationTypeUTF8String
|
|
}
|
|
}
|
|
switch fd.MapValue().Kind() {
|
|
case protoreflect.MessageKind:
|
|
vi.valType = validationTypeMessage
|
|
if ft.Kind() == reflect.Map {
|
|
vi.mi = getMessageInfo(ft.Elem())
|
|
}
|
|
case protoreflect.StringKind:
|
|
if strs.EnforceUTF8(fd) {
|
|
vi.valType = validationTypeUTF8String
|
|
}
|
|
}
|
|
default:
|
|
switch fd.Kind() {
|
|
case protoreflect.MessageKind:
|
|
vi.typ = validationTypeMessage
|
|
if !fd.IsWeak() {
|
|
vi.mi = getMessageInfo(ft)
|
|
}
|
|
case protoreflect.GroupKind:
|
|
vi.typ = validationTypeGroup
|
|
vi.mi = getMessageInfo(ft)
|
|
case protoreflect.StringKind:
|
|
vi.typ = validationTypeBytes
|
|
if strs.EnforceUTF8(fd) {
|
|
vi.typ = validationTypeUTF8String
|
|
}
|
|
default:
|
|
switch wireTypes[fd.Kind()] {
|
|
case protowire.VarintType:
|
|
vi.typ = validationTypeVarint
|
|
case protowire.Fixed32Type:
|
|
vi.typ = validationTypeFixed32
|
|
case protowire.Fixed64Type:
|
|
vi.typ = validationTypeFixed64
|
|
case protowire.BytesType:
|
|
vi.typ = validationTypeBytes
|
|
}
|
|
}
|
|
}
|
|
return vi
|
|
}
|
|
|
|
func (mi *MessageInfo) validate(b []byte, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, result ValidationStatus) {
|
|
mi.init()
|
|
type validationState struct {
|
|
typ validationType
|
|
keyType, valType validationType
|
|
endGroup protowire.Number
|
|
mi *MessageInfo
|
|
tail []byte
|
|
requiredMask uint64
|
|
}
|
|
|
|
// Pre-allocate some slots to avoid repeated slice reallocation.
|
|
states := make([]validationState, 0, 16)
|
|
states = append(states, validationState{
|
|
typ: validationTypeMessage,
|
|
mi: mi,
|
|
})
|
|
if groupTag > 0 {
|
|
states[0].typ = validationTypeGroup
|
|
states[0].endGroup = groupTag
|
|
}
|
|
initialized := true
|
|
start := len(b)
|
|
State:
|
|
for len(states) > 0 {
|
|
st := &states[len(states)-1]
|
|
for len(b) > 0 {
|
|
// Parse the tag (field number and wire type).
|
|
var tag uint64
|
|
if b[0] < 0x80 {
|
|
tag = uint64(b[0])
|
|
b = b[1:]
|
|
} else if len(b) >= 2 && b[1] < 128 {
|
|
tag = uint64(b[0]&0x7f) + uint64(b[1])<<7
|
|
b = b[2:]
|
|
} else {
|
|
var n int
|
|
tag, n = protowire.ConsumeVarint(b)
|
|
if n < 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
b = b[n:]
|
|
}
|
|
var num protowire.Number
|
|
if n := tag >> 3; n < uint64(protowire.MinValidNumber) || n > uint64(protowire.MaxValidNumber) {
|
|
return out, ValidationInvalid
|
|
} else {
|
|
num = protowire.Number(n)
|
|
}
|
|
wtyp := protowire.Type(tag & 7)
|
|
|
|
if wtyp == protowire.EndGroupType {
|
|
if st.endGroup == num {
|
|
goto PopState
|
|
}
|
|
return out, ValidationInvalid
|
|
}
|
|
var vi validationInfo
|
|
switch {
|
|
case st.typ == validationTypeMap:
|
|
switch num {
|
|
case genid.MapEntry_Key_field_number:
|
|
vi.typ = st.keyType
|
|
case genid.MapEntry_Value_field_number:
|
|
vi.typ = st.valType
|
|
vi.mi = st.mi
|
|
vi.requiredBit = 1
|
|
}
|
|
case flags.ProtoLegacy && st.mi.isMessageSet:
|
|
switch num {
|
|
case messageset.FieldItem:
|
|
vi.typ = validationTypeMessageSetItem
|
|
}
|
|
default:
|
|
var f *coderFieldInfo
|
|
if int(num) < len(st.mi.denseCoderFields) {
|
|
f = st.mi.denseCoderFields[num]
|
|
} else {
|
|
f = st.mi.coderFields[num]
|
|
}
|
|
if f != nil {
|
|
vi = f.validation
|
|
if vi.typ == validationTypeMessage && vi.mi == nil {
|
|
// Probable weak field.
|
|
//
|
|
// TODO: Consider storing the results of this lookup somewhere
|
|
// rather than recomputing it on every validation.
|
|
fd := st.mi.Desc.Fields().ByNumber(num)
|
|
if fd == nil || !fd.IsWeak() {
|
|
break
|
|
}
|
|
messageName := fd.Message().FullName()
|
|
messageType, err := protoregistry.GlobalTypes.FindMessageByName(messageName)
|
|
switch err {
|
|
case nil:
|
|
vi.mi, _ = messageType.(*MessageInfo)
|
|
case protoregistry.NotFound:
|
|
vi.typ = validationTypeBytes
|
|
default:
|
|
return out, ValidationUnknown
|
|
}
|
|
}
|
|
break
|
|
}
|
|
// Possible extension field.
|
|
//
|
|
// TODO: We should return ValidationUnknown when:
|
|
// 1. The resolver is not frozen. (More extensions may be added to it.)
|
|
// 2. The resolver returns preg.NotFound.
|
|
// In this case, a type added to the resolver in the future could cause
|
|
// unmarshaling to begin failing. Supporting this requires some way to
|
|
// determine if the resolver is frozen.
|
|
xt, err := opts.resolver.FindExtensionByNumber(st.mi.Desc.FullName(), num)
|
|
if err != nil && err != protoregistry.NotFound {
|
|
return out, ValidationUnknown
|
|
}
|
|
if err == nil {
|
|
vi = getExtensionFieldInfo(xt).validation
|
|
}
|
|
}
|
|
if vi.requiredBit != 0 {
|
|
// Check that the field has a compatible wire type.
|
|
// We only need to consider non-repeated field types,
|
|
// since repeated fields (and maps) can never be required.
|
|
ok := false
|
|
switch vi.typ {
|
|
case validationTypeVarint:
|
|
ok = wtyp == protowire.VarintType
|
|
case validationTypeFixed32:
|
|
ok = wtyp == protowire.Fixed32Type
|
|
case validationTypeFixed64:
|
|
ok = wtyp == protowire.Fixed64Type
|
|
case validationTypeBytes, validationTypeUTF8String, validationTypeMessage:
|
|
ok = wtyp == protowire.BytesType
|
|
case validationTypeGroup:
|
|
ok = wtyp == protowire.StartGroupType
|
|
}
|
|
if ok {
|
|
st.requiredMask |= vi.requiredBit
|
|
}
|
|
}
|
|
|
|
switch wtyp {
|
|
case protowire.VarintType:
|
|
if len(b) >= 10 {
|
|
switch {
|
|
case b[0] < 0x80:
|
|
b = b[1:]
|
|
case b[1] < 0x80:
|
|
b = b[2:]
|
|
case b[2] < 0x80:
|
|
b = b[3:]
|
|
case b[3] < 0x80:
|
|
b = b[4:]
|
|
case b[4] < 0x80:
|
|
b = b[5:]
|
|
case b[5] < 0x80:
|
|
b = b[6:]
|
|
case b[6] < 0x80:
|
|
b = b[7:]
|
|
case b[7] < 0x80:
|
|
b = b[8:]
|
|
case b[8] < 0x80:
|
|
b = b[9:]
|
|
case b[9] < 0x80 && b[9] < 2:
|
|
b = b[10:]
|
|
default:
|
|
return out, ValidationInvalid
|
|
}
|
|
} else {
|
|
switch {
|
|
case len(b) > 0 && b[0] < 0x80:
|
|
b = b[1:]
|
|
case len(b) > 1 && b[1] < 0x80:
|
|
b = b[2:]
|
|
case len(b) > 2 && b[2] < 0x80:
|
|
b = b[3:]
|
|
case len(b) > 3 && b[3] < 0x80:
|
|
b = b[4:]
|
|
case len(b) > 4 && b[4] < 0x80:
|
|
b = b[5:]
|
|
case len(b) > 5 && b[5] < 0x80:
|
|
b = b[6:]
|
|
case len(b) > 6 && b[6] < 0x80:
|
|
b = b[7:]
|
|
case len(b) > 7 && b[7] < 0x80:
|
|
b = b[8:]
|
|
case len(b) > 8 && b[8] < 0x80:
|
|
b = b[9:]
|
|
case len(b) > 9 && b[9] < 2:
|
|
b = b[10:]
|
|
default:
|
|
return out, ValidationInvalid
|
|
}
|
|
}
|
|
continue State
|
|
case protowire.BytesType:
|
|
var size uint64
|
|
if len(b) >= 1 && b[0] < 0x80 {
|
|
size = uint64(b[0])
|
|
b = b[1:]
|
|
} else if len(b) >= 2 && b[1] < 128 {
|
|
size = uint64(b[0]&0x7f) + uint64(b[1])<<7
|
|
b = b[2:]
|
|
} else {
|
|
var n int
|
|
size, n = protowire.ConsumeVarint(b)
|
|
if n < 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
b = b[n:]
|
|
}
|
|
if size > uint64(len(b)) {
|
|
return out, ValidationInvalid
|
|
}
|
|
v := b[:size]
|
|
b = b[size:]
|
|
switch vi.typ {
|
|
case validationTypeMessage:
|
|
if vi.mi == nil {
|
|
return out, ValidationUnknown
|
|
}
|
|
vi.mi.init()
|
|
fallthrough
|
|
case validationTypeMap:
|
|
if vi.mi != nil {
|
|
vi.mi.init()
|
|
}
|
|
states = append(states, validationState{
|
|
typ: vi.typ,
|
|
keyType: vi.keyType,
|
|
valType: vi.valType,
|
|
mi: vi.mi,
|
|
tail: b,
|
|
})
|
|
b = v
|
|
continue State
|
|
case validationTypeRepeatedVarint:
|
|
// Packed field.
|
|
for len(v) > 0 {
|
|
_, n := protowire.ConsumeVarint(v)
|
|
if n < 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
v = v[n:]
|
|
}
|
|
case validationTypeRepeatedFixed32:
|
|
// Packed field.
|
|
if len(v)%4 != 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
case validationTypeRepeatedFixed64:
|
|
// Packed field.
|
|
if len(v)%8 != 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
case validationTypeUTF8String:
|
|
if !utf8.Valid(v) {
|
|
return out, ValidationInvalid
|
|
}
|
|
}
|
|
case protowire.Fixed32Type:
|
|
if len(b) < 4 {
|
|
return out, ValidationInvalid
|
|
}
|
|
b = b[4:]
|
|
case protowire.Fixed64Type:
|
|
if len(b) < 8 {
|
|
return out, ValidationInvalid
|
|
}
|
|
b = b[8:]
|
|
case protowire.StartGroupType:
|
|
switch {
|
|
case vi.typ == validationTypeGroup:
|
|
if vi.mi == nil {
|
|
return out, ValidationUnknown
|
|
}
|
|
vi.mi.init()
|
|
states = append(states, validationState{
|
|
typ: validationTypeGroup,
|
|
mi: vi.mi,
|
|
endGroup: num,
|
|
})
|
|
continue State
|
|
case flags.ProtoLegacy && vi.typ == validationTypeMessageSetItem:
|
|
typeid, v, n, err := messageset.ConsumeFieldValue(b, false)
|
|
if err != nil {
|
|
return out, ValidationInvalid
|
|
}
|
|
xt, err := opts.resolver.FindExtensionByNumber(st.mi.Desc.FullName(), typeid)
|
|
switch {
|
|
case err == protoregistry.NotFound:
|
|
b = b[n:]
|
|
case err != nil:
|
|
return out, ValidationUnknown
|
|
default:
|
|
xvi := getExtensionFieldInfo(xt).validation
|
|
if xvi.mi != nil {
|
|
xvi.mi.init()
|
|
}
|
|
states = append(states, validationState{
|
|
typ: xvi.typ,
|
|
mi: xvi.mi,
|
|
tail: b[n:],
|
|
})
|
|
b = v
|
|
continue State
|
|
}
|
|
default:
|
|
n := protowire.ConsumeFieldValue(num, wtyp, b)
|
|
if n < 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
b = b[n:]
|
|
}
|
|
default:
|
|
return out, ValidationInvalid
|
|
}
|
|
}
|
|
if st.endGroup != 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
if len(b) != 0 {
|
|
return out, ValidationInvalid
|
|
}
|
|
b = st.tail
|
|
PopState:
|
|
numRequiredFields := 0
|
|
switch st.typ {
|
|
case validationTypeMessage, validationTypeGroup:
|
|
numRequiredFields = int(st.mi.numRequiredFields)
|
|
case validationTypeMap:
|
|
// If this is a map field with a message value that contains
|
|
// required fields, require that the value be present.
|
|
if st.mi != nil && st.mi.numRequiredFields > 0 {
|
|
numRequiredFields = 1
|
|
}
|
|
}
|
|
// If there are more than 64 required fields, this check will
|
|
// always fail and we will report that the message is potentially
|
|
// uninitialized.
|
|
if numRequiredFields > 0 && bits.OnesCount64(st.requiredMask) != numRequiredFields {
|
|
initialized = false
|
|
}
|
|
states = states[:len(states)-1]
|
|
}
|
|
out.n = start - len(b)
|
|
if initialized {
|
|
out.initialized = true
|
|
}
|
|
return out, ValidationValid
|
|
}
|