mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 13:28:25 +00:00 
			
		
		
		
	* update code.gitea.io/sdk/gitea v0.13.1 -> v0.13.2 * update github.com/go-swagger/go-swagger v0.25.0 -> v0.26.0 * update github.com/google/uuid v1.1.2 -> v1.2.0 * update github.com/klauspost/compress v1.11.3 -> v1.11.7 * update github.com/lib/pq 083382b7e6fc -> v1.9.0 * update github.com/markbates/goth v1.65.0 -> v1.66.1 * update github.com/mattn/go-sqlite3 v1.14.4 -> v1.14.6 * update github.com/mgechev/revive 246eac737dc7 -> v1.0.3 * update github.com/minio/minio-go/v7 v7.0.6 -> v7.0.7 * update github.com/niklasfasching/go-org v1.3.2 -> v1.4.0 * update github.com/olivere/elastic/v7 v7.0.21 -> v7.0.22 * update github.com/pquerna/otp v1.2.0 -> v1.3.0 * update github.com/xanzy/go-gitlab v0.39.0 -> v0.42.0 * update github.com/yuin/goldmark v1.2.1 -> v1.3.1
		
			
				
	
	
		
			280 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
// Copyright 2015 go-swagger maintainers
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
//    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
package validate
 | 
						|
 | 
						|
import (
 | 
						|
	"reflect"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/go-openapi/errors"
 | 
						|
	"github.com/go-openapi/spec"
 | 
						|
	"github.com/go-openapi/strfmt"
 | 
						|
)
 | 
						|
 | 
						|
type objectValidator struct {
 | 
						|
	Path                 string
 | 
						|
	In                   string
 | 
						|
	MaxProperties        *int64
 | 
						|
	MinProperties        *int64
 | 
						|
	Required             []string
 | 
						|
	Properties           map[string]spec.Schema
 | 
						|
	AdditionalProperties *spec.SchemaOrBool
 | 
						|
	PatternProperties    map[string]spec.Schema
 | 
						|
	Root                 interface{}
 | 
						|
	KnownFormats         strfmt.Registry
 | 
						|
	Options              SchemaValidatorOptions
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) SetPath(path string) {
 | 
						|
	o.Path = path
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool {
 | 
						|
	// TODO: this should also work for structs
 | 
						|
	// there is a problem in the type validator where it will be unhappy about null values
 | 
						|
	// so that requires more testing
 | 
						|
	r := reflect.TypeOf(source) == specSchemaType && (kind == reflect.Map || kind == reflect.Struct)
 | 
						|
	debugLog("object validator for %q applies %t for %T (kind: %v)\n", o.Path, r, source, kind)
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) isProperties() bool {
 | 
						|
	p := strings.Split(o.Path, ".")
 | 
						|
	return len(p) > 1 && p[len(p)-1] == jsonProperties && p[len(p)-2] != jsonProperties
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) isDefault() bool {
 | 
						|
	p := strings.Split(o.Path, ".")
 | 
						|
	return len(p) > 1 && p[len(p)-1] == jsonDefault && p[len(p)-2] != jsonDefault
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) isExample() bool {
 | 
						|
	p := strings.Split(o.Path, ".")
 | 
						|
	return len(p) > 1 && (p[len(p)-1] == swaggerExample || p[len(p)-1] == swaggerExamples) && p[len(p)-2] != swaggerExample
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) checkArrayMustHaveItems(res *Result, val map[string]interface{}) {
 | 
						|
	// for swagger 2.0 schemas, there is an additional constraint to have array items defined explicitly.
 | 
						|
	// with pure jsonschema draft 4, one may have arrays with undefined items (i.e. any type).
 | 
						|
	if t, typeFound := val[jsonType]; typeFound {
 | 
						|
		if tpe, ok := t.(string); ok && tpe == arrayType {
 | 
						|
			if item, itemsKeyFound := val[jsonItems]; !itemsKeyFound {
 | 
						|
				res.AddErrors(errors.Required(jsonItems, o.Path, item))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) checkItemsMustBeTypeArray(res *Result, val map[string]interface{}) {
 | 
						|
	if !o.isProperties() && !o.isDefault() && !o.isExample() {
 | 
						|
		if _, itemsKeyFound := val[jsonItems]; itemsKeyFound {
 | 
						|
			t, typeFound := val[jsonType]
 | 
						|
			if typeFound {
 | 
						|
				if tpe, ok := t.(string); !ok || tpe != arrayType {
 | 
						|
					res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				// there is no type
 | 
						|
				res.AddErrors(errors.Required(jsonType, o.Path, t))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) precheck(res *Result, val map[string]interface{}) {
 | 
						|
	if o.Options.EnableArrayMustHaveItemsCheck {
 | 
						|
		o.checkArrayMustHaveItems(res, val)
 | 
						|
	}
 | 
						|
	if o.Options.EnableObjectArrayTypeCheck {
 | 
						|
		o.checkItemsMustBeTypeArray(res, val)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (o *objectValidator) Validate(data interface{}) *Result {
 | 
						|
	val := data.(map[string]interface{})
 | 
						|
	// TODO: guard against nil data
 | 
						|
	numKeys := int64(len(val))
 | 
						|
 | 
						|
	if o.MinProperties != nil && numKeys < *o.MinProperties {
 | 
						|
		return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties))
 | 
						|
	}
 | 
						|
	if o.MaxProperties != nil && numKeys > *o.MaxProperties {
 | 
						|
		return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties))
 | 
						|
	}
 | 
						|
 | 
						|
	res := new(Result)
 | 
						|
 | 
						|
	o.precheck(res, val)
 | 
						|
 | 
						|
	// check validity of field names
 | 
						|
	if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows {
 | 
						|
		// Case: additionalProperties: false
 | 
						|
		for k := range val {
 | 
						|
			_, regularProperty := o.Properties[k]
 | 
						|
			matched := false
 | 
						|
 | 
						|
			for pk := range o.PatternProperties {
 | 
						|
				if matches, _ := regexp.MatchString(pk, k); matches {
 | 
						|
					matched = true
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if !regularProperty && k != "$schema" && k != "id" && !matched {
 | 
						|
				// Special properties "$schema" and "id" are ignored
 | 
						|
				res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
 | 
						|
 | 
						|
				// BUG(fredbi): This section should move to a part dedicated to spec validation as
 | 
						|
				// it will conflict with regular schemas where a property "headers" is defined.
 | 
						|
 | 
						|
				//
 | 
						|
				// Croaks a more explicit message on top of the standard one
 | 
						|
				// on some recognized cases.
 | 
						|
				//
 | 
						|
				// NOTE: edge cases with invalid type assertion are simply ignored here.
 | 
						|
				// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
 | 
						|
				// by higher level callers (the IMPORTANT! tag will be eventually
 | 
						|
				// removed).
 | 
						|
				if k == "headers" && val[k] != nil {
 | 
						|
					// $ref is forbidden in header
 | 
						|
					if headers, mapOk := val[k].(map[string]interface{}); mapOk {
 | 
						|
						for headerKey, headerBody := range headers {
 | 
						|
							if headerBody != nil {
 | 
						|
								if headerSchema, mapOfMapOk := headerBody.(map[string]interface{}); mapOfMapOk {
 | 
						|
									if _, found := headerSchema["$ref"]; found {
 | 
						|
										var msg string
 | 
						|
										if refString, stringOk := headerSchema["$ref"].(string); stringOk {
 | 
						|
											msg = strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
 | 
						|
										}
 | 
						|
										res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
 | 
						|
									}
 | 
						|
								}
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
					/*
 | 
						|
						case "$ref":
 | 
						|
							if val[k] != nil {
 | 
						|
								// TODO: check context of that ref: warn about siblings, check against invalid context
 | 
						|
							}
 | 
						|
					*/
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		// Cases: no additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
 | 
						|
		for key, value := range val {
 | 
						|
			_, regularProperty := o.Properties[key]
 | 
						|
 | 
						|
			// Validates property against "patternProperties" if applicable
 | 
						|
			// BUG(fredbi): succeededOnce is always false
 | 
						|
 | 
						|
			// NOTE: how about regular properties which do not match patternProperties?
 | 
						|
			matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
 | 
						|
 | 
						|
			if !(regularProperty || matched || succeededOnce) {
 | 
						|
 | 
						|
				// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
 | 
						|
				if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil {
 | 
						|
					// AdditionalProperties as Schema
 | 
						|
					r := NewSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
 | 
						|
					res.mergeForField(data.(map[string]interface{}), key, r)
 | 
						|
				} else if regularProperty && !(matched || succeededOnce) {
 | 
						|
					// TODO: this is dead code since regularProperty=false here
 | 
						|
					res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key))
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Valid cases: additionalProperties: true or undefined
 | 
						|
	}
 | 
						|
 | 
						|
	createdFromDefaults := map[string]bool{}
 | 
						|
 | 
						|
	// Property types:
 | 
						|
	// - regular Property
 | 
						|
	for pName := range o.Properties {
 | 
						|
		pSchema := o.Properties[pName] // one instance per iteration
 | 
						|
		rName := pName
 | 
						|
		if o.Path != "" {
 | 
						|
			rName = o.Path + "." + pName
 | 
						|
		}
 | 
						|
 | 
						|
		// Recursively validates each property against its schema
 | 
						|
		if v, ok := val[pName]; ok {
 | 
						|
			r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v)
 | 
						|
			res.mergeForField(data.(map[string]interface{}), pName, r)
 | 
						|
		} else if pSchema.Default != nil {
 | 
						|
			// If a default value is defined, creates the property from defaults
 | 
						|
			// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
 | 
						|
			createdFromDefaults[pName] = true
 | 
						|
			res.addPropertySchemata(data.(map[string]interface{}), pName, &pSchema)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Check required properties
 | 
						|
	if len(o.Required) > 0 {
 | 
						|
		for _, k := range o.Required {
 | 
						|
			if v, ok := val[k]; !ok && !createdFromDefaults[k] {
 | 
						|
				res.AddErrors(errors.Required(o.Path+"."+k, o.In, v))
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Check patternProperties
 | 
						|
	// TODO: it looks like we have done that twice in many cases
 | 
						|
	for key, value := range val {
 | 
						|
		_, regularProperty := o.Properties[key]
 | 
						|
		matched, _ /*succeededOnce*/, patterns := o.validatePatternProperty(key, value, res)
 | 
						|
		if !regularProperty && (matched /*|| succeededOnce*/) {
 | 
						|
			for _, pName := range patterns {
 | 
						|
				if v, ok := o.PatternProperties[pName]; ok {
 | 
						|
					r := NewSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
 | 
						|
					res.mergeForField(data.(map[string]interface{}), key, r)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return res
 | 
						|
}
 | 
						|
 | 
						|
// TODO: succeededOnce is not used anywhere
 | 
						|
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
 | 
						|
	matched := false
 | 
						|
	succeededOnce := false
 | 
						|
	var patterns []string
 | 
						|
 | 
						|
	for k, schema := range o.PatternProperties {
 | 
						|
		sch := schema
 | 
						|
		if match, _ := regexp.MatchString(k, key); match {
 | 
						|
			patterns = append(patterns, k)
 | 
						|
			matched = true
 | 
						|
			validator := NewSchemaValidator(&sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...)
 | 
						|
 | 
						|
			res := validator.Validate(value)
 | 
						|
			result.Merge(res)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// BUG(fredbi): can't get to here. Should remove dead code (commented out).
 | 
						|
 | 
						|
	// if succeededOnce {
 | 
						|
	//	result.Inc()
 | 
						|
	// }
 | 
						|
 | 
						|
	return matched, succeededOnce, patterns
 | 
						|
}
 |