podman
281 строка · 9.4 Кб
1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package validate
16
17import (
18"fmt"
19"strings"
20
21"github.com/go-openapi/spec"
22)
23
24// defaultValidator validates default values in a spec.
25// According to Swagger spec, default values MUST validate their schema.
26type defaultValidator struct {
27SpecValidator *SpecValidator
28visitedSchemas map[string]bool
29}
30
31// resetVisited resets the internal state of visited schemas
32func (d *defaultValidator) resetVisited() {
33d.visitedSchemas = map[string]bool{}
34}
35
36func isVisited(path string, visitedSchemas map[string]bool) bool {
37found := visitedSchemas[path]
38if !found {
39// search for overlapping paths
40frags := strings.Split(path, ".")
41if len(frags) < 2 {
42// shortcut exit on smaller paths
43return found
44}
45last := len(frags) - 1
46var currentFragStr, parent string
47for i := range frags {
48if i == 0 {
49currentFragStr = frags[last]
50} else {
51currentFragStr = strings.Join([]string{frags[last-i], currentFragStr}, ".")
52}
53if i < last {
54parent = strings.Join(frags[0:last-i], ".")
55} else {
56parent = ""
57}
58if strings.HasSuffix(parent, currentFragStr) {
59found = true
60break
61}
62}
63}
64return found
65}
66
67// beingVisited asserts a schema is being visited
68func (d *defaultValidator) beingVisited(path string) {
69d.visitedSchemas[path] = true
70}
71
72// isVisited tells if a path has already been visited
73func (d *defaultValidator) isVisited(path string) bool {
74return isVisited(path, d.visitedSchemas)
75}
76
77// Validate validates the default values declared in the swagger spec
78func (d *defaultValidator) Validate() (errs *Result) {
79errs = new(Result)
80if d == nil || d.SpecValidator == nil {
81return errs
82}
83d.resetVisited()
84errs.Merge(d.validateDefaultValueValidAgainstSchema()) // error -
85return errs
86}
87
88func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
89// every default value that is specified must validate against the schema for that property
90// headers, items, parameters, schema
91
92res := new(Result)
93s := d.SpecValidator
94
95for method, pathItem := range s.expandedAnalyzer().Operations() {
96for path, op := range pathItem {
97// parameters
98for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
99if param.Default != nil && param.Required {
100res.AddWarnings(requiredHasDefaultMsg(param.Name, param.In))
101}
102
103// reset explored schemas to get depth-first recursive-proof exploration
104d.resetVisited()
105
106// Check simple parameters first
107// default values provided must validate against their inline definition (no explicit schema)
108if param.Default != nil && param.Schema == nil {
109// check param default value is valid
110red := NewParamValidator(¶m, s.KnownFormats).Validate(param.Default) //#nosec
111if red.HasErrorsOrWarnings() {
112res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
113res.Merge(red)
114}
115}
116
117// Recursively follows Items and Schemas
118if param.Items != nil {
119red := d.validateDefaultValueItemsAgainstSchema(param.Name, param.In, ¶m, param.Items) //#nosec
120if red.HasErrorsOrWarnings() {
121res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
122res.Merge(red)
123}
124}
125
126if param.Schema != nil {
127// Validate default value against schema
128red := d.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
129if red.HasErrorsOrWarnings() {
130res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
131res.Merge(red)
132}
133}
134}
135
136if op.Responses != nil {
137if op.Responses.Default != nil {
138// Same constraint on default Response
139res.Merge(d.validateDefaultInResponse(op.Responses.Default, jsonDefault, path, 0, op.ID))
140}
141// Same constraint on regular Responses
142if op.Responses.StatusCodeResponses != nil { // Safeguard
143for code, r := range op.Responses.StatusCodeResponses {
144res.Merge(d.validateDefaultInResponse(&r, "response", path, code, op.ID)) //#nosec
145}
146}
147} else if op.ID != "" {
148// Empty op.ID means there is no meaningful operation: no need to report a specific message
149res.AddErrors(noValidResponseMsg(op.ID))
150}
151}
152}
153if s.spec.Spec().Definitions != nil { // Safeguard
154// reset explored schemas to get depth-first recursive-proof exploration
155d.resetVisited()
156for nm, sch := range s.spec.Spec().Definitions {
157res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) //#nosec
158}
159}
160return res
161}
162
163func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, responseType, path string, responseCode int, operationID string) *Result {
164s := d.SpecValidator
165
166response, res := responseHelp.expandResponseRef(resp, path, s)
167if !res.IsValid() {
168return res
169}
170
171responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode)
172
173// nolint: dupl
174if response.Headers != nil { // Safeguard
175for nm, h := range response.Headers {
176// reset explored schemas to get depth-first recursive-proof exploration
177d.resetVisited()
178
179if h.Default != nil {
180red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default) //#nosec
181if red.HasErrorsOrWarnings() {
182res.AddErrors(defaultValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
183res.Merge(red)
184}
185}
186
187// Headers have inline definition, like params
188if h.Items != nil {
189red := d.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items) //#nosec
190if red.HasErrorsOrWarnings() {
191res.AddErrors(defaultValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
192res.Merge(red)
193}
194}
195
196if _, err := compileRegexp(h.Pattern); err != nil {
197res.AddErrors(invalidPatternInHeaderMsg(operationID, nm, responseName, h.Pattern, err))
198}
199
200// Headers don't have schema
201}
202}
203if response.Schema != nil {
204// reset explored schemas to get depth-first recursive-proof exploration
205d.resetVisited()
206
207red := d.validateDefaultValueSchemaAgainstSchema(responseCodeAsStr, "response", response.Schema)
208if red.HasErrorsOrWarnings() {
209// Additional message to make sure the context of the error is not lost
210res.AddErrors(defaultValueInDoesNotValidateMsg(operationID, responseName))
211res.Merge(red)
212}
213}
214return res
215}
216
217func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in string, schema *spec.Schema) *Result {
218if schema == nil || d.isVisited(path) {
219// Avoids recursing if we are already done with that check
220return nil
221}
222d.beingVisited(path)
223res := new(Result)
224s := d.SpecValidator
225
226if schema.Default != nil {
227res.Merge(NewSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats, SwaggerSchema(true)).Validate(schema.Default))
228}
229if schema.Items != nil {
230if schema.Items.Schema != nil {
231res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".items.default", in, schema.Items.Schema))
232}
233// Multiple schemas in items
234if schema.Items.Schemas != nil { // Safeguard
235for i, sch := range schema.Items.Schemas {
236res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.items[%d].default", path, i), in, &sch)) //#nosec
237}
238}
239}
240if _, err := compileRegexp(schema.Pattern); err != nil {
241res.AddErrors(invalidPatternInMsg(path, in, schema.Pattern))
242}
243if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
244// NOTE: we keep validating values, even though additionalItems is not supported by Swagger 2.0 (and 3.0 as well)
245res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema))
246}
247for propName, prop := range schema.Properties {
248res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
249}
250for propName, prop := range schema.PatternProperties {
251res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
252}
253if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
254res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema))
255}
256if schema.AllOf != nil {
257for i, aoSch := range schema.AllOf {
258res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.allOf[%d]", path, i), in, &aoSch)) //#nosec
259}
260}
261return res
262}
263
264// TODO: Temporary duplicated code. Need to refactor with examples
265// nolint: dupl
266func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
267res := new(Result)
268s := d.SpecValidator
269if items != nil {
270if items.Default != nil {
271res.Merge(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Default))
272}
273if items.Items != nil {
274res.Merge(d.validateDefaultValueItemsAgainstSchema(path+"[0].default", in, root, items.Items))
275}
276if _, err := compileRegexp(items.Pattern); err != nil {
277res.AddErrors(invalidPatternInMsg(path, in, items.Pattern))
278}
279}
280return res
281}
282