mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-18 12:20:24 +00:00
503 lines
11 KiB
Go
503 lines
11 KiB
Go
|
/*
|
||
|
Copyright 2022 The Kubernetes Authors.
|
||
|
|
||
|
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 spec
|
||
|
|
||
|
import (
|
||
|
"github.com/go-openapi/jsonreference"
|
||
|
"github.com/google/go-cmp/cmp"
|
||
|
fuzz "github.com/google/gofuzz"
|
||
|
)
|
||
|
|
||
|
var SwaggerFuzzFuncs []interface{} = []interface{}{
|
||
|
func(v *Responses, c fuzz.Continue) {
|
||
|
c.FuzzNoCustom(v)
|
||
|
if v.Default != nil {
|
||
|
// Check if we hit maxDepth and left an incomplete value
|
||
|
if v.Default.Description == "" {
|
||
|
v.Default = nil
|
||
|
v.StatusCodeResponses = nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// conversion has no way to discern empty statusCodeResponses from
|
||
|
// nil, since "default" is always included in the map.
|
||
|
// So avoid empty responses list
|
||
|
if len(v.StatusCodeResponses) == 0 {
|
||
|
v.StatusCodeResponses = nil
|
||
|
}
|
||
|
},
|
||
|
func(v *Operation, c fuzz.Continue) {
|
||
|
c.FuzzNoCustom(v)
|
||
|
|
||
|
if v != nil {
|
||
|
// force non-nil
|
||
|
v.Responses = &Responses{}
|
||
|
c.Fuzz(v.Responses)
|
||
|
|
||
|
v.Schemes = nil
|
||
|
if c.RandBool() {
|
||
|
v.Schemes = append(v.Schemes, "http")
|
||
|
}
|
||
|
|
||
|
if c.RandBool() {
|
||
|
v.Schemes = append(v.Schemes, "https")
|
||
|
}
|
||
|
|
||
|
if c.RandBool() {
|
||
|
v.Schemes = append(v.Schemes, "ws")
|
||
|
}
|
||
|
|
||
|
if c.RandBool() {
|
||
|
v.Schemes = append(v.Schemes, "wss")
|
||
|
}
|
||
|
|
||
|
// Gnostic unconditionally makes security values non-null
|
||
|
// So do not fuzz null values into the array.
|
||
|
for i, val := range v.Security {
|
||
|
if val == nil {
|
||
|
v.Security[i] = make(map[string][]string)
|
||
|
}
|
||
|
|
||
|
for k, v := range val {
|
||
|
if v == nil {
|
||
|
val[k] = make([]string, 0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
func(v map[int]Response, c fuzz.Continue) {
|
||
|
n := 0
|
||
|
c.Fuzz(&n)
|
||
|
if n == 0 {
|
||
|
// Test that fuzzer is not at maxDepth so we do not
|
||
|
// end up with empty elements
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Prevent negative numbers
|
||
|
num := c.Intn(4)
|
||
|
for i := 0; i < num+2; i++ {
|
||
|
val := Response{}
|
||
|
c.Fuzz(&val)
|
||
|
|
||
|
val.Description = c.RandString() + "x"
|
||
|
v[100*(i+1)+c.Intn(100)] = val
|
||
|
}
|
||
|
},
|
||
|
func(v map[string]PathItem, c fuzz.Continue) {
|
||
|
n := 0
|
||
|
c.Fuzz(&n)
|
||
|
if n == 0 {
|
||
|
// Test that fuzzer is not at maxDepth so we do not
|
||
|
// end up with empty elements
|
||
|
return
|
||
|
}
|
||
|
|
||
|
num := c.Intn(5)
|
||
|
for i := 0; i < num+2; i++ {
|
||
|
val := PathItem{}
|
||
|
c.Fuzz(&val)
|
||
|
|
||
|
// Ref params are only allowed in certain locations, so
|
||
|
// possibly add a few to PathItems
|
||
|
numRefsToAdd := c.Intn(5)
|
||
|
for i := 0; i < numRefsToAdd; i++ {
|
||
|
theRef := Parameter{}
|
||
|
c.Fuzz(&theRef.Refable)
|
||
|
|
||
|
val.Parameters = append(val.Parameters, theRef)
|
||
|
}
|
||
|
|
||
|
v["/"+c.RandString()] = val
|
||
|
}
|
||
|
},
|
||
|
func(v *SchemaOrArray, c fuzz.Continue) {
|
||
|
*v = SchemaOrArray{}
|
||
|
// gnostic parser just doesn't support more
|
||
|
// than one Schema here
|
||
|
v.Schema = &Schema{}
|
||
|
c.Fuzz(&v.Schema)
|
||
|
|
||
|
},
|
||
|
func(v *SchemaOrBool, c fuzz.Continue) {
|
||
|
*v = SchemaOrBool{}
|
||
|
|
||
|
if c.RandBool() {
|
||
|
v.Allows = c.RandBool()
|
||
|
} else {
|
||
|
v.Schema = &Schema{}
|
||
|
v.Allows = true
|
||
|
c.Fuzz(&v.Schema)
|
||
|
}
|
||
|
},
|
||
|
func(v map[string]Response, c fuzz.Continue) {
|
||
|
n := 0
|
||
|
c.Fuzz(&n)
|
||
|
if n == 0 {
|
||
|
// Test that fuzzer is not at maxDepth so we do not
|
||
|
// end up with empty elements
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Response definitions are not allowed to
|
||
|
// be refs
|
||
|
for i := 0; i < c.Intn(5)+1; i++ {
|
||
|
resp := &Response{}
|
||
|
|
||
|
c.Fuzz(resp)
|
||
|
resp.Ref = Ref{}
|
||
|
resp.Description = c.RandString() + "x"
|
||
|
|
||
|
// Response refs are not vendor extensible by gnostic
|
||
|
resp.VendorExtensible.Extensions = nil
|
||
|
v[c.RandString()+"x"] = *resp
|
||
|
}
|
||
|
},
|
||
|
func(v *Header, c fuzz.Continue) {
|
||
|
if v != nil {
|
||
|
c.FuzzNoCustom(v)
|
||
|
|
||
|
// descendant Items of Header may not be refs
|
||
|
cur := v.Items
|
||
|
for cur != nil {
|
||
|
cur.Ref = Ref{}
|
||
|
cur = cur.Items
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
func(v *Ref, c fuzz.Continue) {
|
||
|
*v = Ref{}
|
||
|
v.Ref, _ = jsonreference.New("http://asd.com/" + c.RandString())
|
||
|
},
|
||
|
func(v *Response, c fuzz.Continue) {
|
||
|
*v = Response{}
|
||
|
if c.RandBool() {
|
||
|
v.Ref = Ref{}
|
||
|
v.Ref.Ref, _ = jsonreference.New("http://asd.com/" + c.RandString())
|
||
|
} else {
|
||
|
c.Fuzz(&v.VendorExtensible)
|
||
|
c.Fuzz(&v.Schema)
|
||
|
c.Fuzz(&v.ResponseProps)
|
||
|
|
||
|
v.Headers = nil
|
||
|
v.Ref = Ref{}
|
||
|
|
||
|
n := 0
|
||
|
c.Fuzz(&n)
|
||
|
if n != 0 {
|
||
|
// Test that fuzzer is not at maxDepth so we do not
|
||
|
// end up with empty elements
|
||
|
num := c.Intn(4)
|
||
|
for i := 0; i < num; i++ {
|
||
|
if v.Headers == nil {
|
||
|
v.Headers = make(map[string]Header)
|
||
|
}
|
||
|
hdr := Header{}
|
||
|
c.Fuzz(&hdr)
|
||
|
if hdr.Type == "" {
|
||
|
// hit maxDepth, just abort trying to make haders
|
||
|
v.Headers = nil
|
||
|
break
|
||
|
}
|
||
|
v.Headers[c.RandString()+"x"] = hdr
|
||
|
}
|
||
|
} else {
|
||
|
v.Headers = nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
v.Description = c.RandString() + "x"
|
||
|
|
||
|
// Gnostic parses empty as nil, so to keep avoid putting empty
|
||
|
if len(v.Headers) == 0 {
|
||
|
v.Headers = nil
|
||
|
}
|
||
|
},
|
||
|
func(v **Info, c fuzz.Continue) {
|
||
|
// Info is never nil
|
||
|
*v = &Info{}
|
||
|
c.FuzzNoCustom(*v)
|
||
|
|
||
|
(*v).Title = c.RandString() + "x"
|
||
|
},
|
||
|
func(v *Extensions, c fuzz.Continue) {
|
||
|
// gnostic parser only picks up x- vendor extensions
|
||
|
numChildren := c.Intn(5)
|
||
|
for i := 0; i < numChildren; i++ {
|
||
|
if *v == nil {
|
||
|
*v = Extensions{}
|
||
|
}
|
||
|
(*v)["x-"+c.RandString()] = c.RandString()
|
||
|
}
|
||
|
},
|
||
|
func(v *Swagger, c fuzz.Continue) {
|
||
|
c.FuzzNoCustom(v)
|
||
|
|
||
|
if v.Paths == nil {
|
||
|
// Force paths non-nil since it does not have omitempty in json tag.
|
||
|
// This means a perfect roundtrip (via json) is impossible,
|
||
|
// since we can't tell the difference between empty/unspecified paths
|
||
|
v.Paths = &Paths{}
|
||
|
c.Fuzz(v.Paths)
|
||
|
}
|
||
|
|
||
|
v.Swagger = "2.0"
|
||
|
|
||
|
// Gnostic support serializing ID at all
|
||
|
// unavoidable data loss
|
||
|
v.ID = ""
|
||
|
|
||
|
v.Schemes = nil
|
||
|
if c.RandUint64()%2 == 1 {
|
||
|
v.Schemes = append(v.Schemes, "http")
|
||
|
}
|
||
|
|
||
|
if c.RandUint64()%2 == 1 {
|
||
|
v.Schemes = append(v.Schemes, "https")
|
||
|
}
|
||
|
|
||
|
if c.RandUint64()%2 == 1 {
|
||
|
v.Schemes = append(v.Schemes, "ws")
|
||
|
}
|
||
|
|
||
|
if c.RandUint64()%2 == 1 {
|
||
|
v.Schemes = append(v.Schemes, "wss")
|
||
|
}
|
||
|
|
||
|
// Gnostic unconditionally makes security values non-null
|
||
|
// So do not fuzz null values into the array.
|
||
|
for i, val := range v.Security {
|
||
|
if val == nil {
|
||
|
v.Security[i] = make(map[string][]string)
|
||
|
}
|
||
|
|
||
|
for k, v := range val {
|
||
|
if v == nil {
|
||
|
val[k] = make([]string, 0)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
func(v *SecurityScheme, c fuzz.Continue) {
|
||
|
v.Description = c.RandString() + "x"
|
||
|
c.Fuzz(&v.VendorExtensible)
|
||
|
|
||
|
switch c.Intn(3) {
|
||
|
case 0:
|
||
|
v.Type = "basic"
|
||
|
case 1:
|
||
|
v.Type = "apiKey"
|
||
|
switch c.Intn(2) {
|
||
|
case 0:
|
||
|
v.In = "header"
|
||
|
case 1:
|
||
|
v.In = "query"
|
||
|
default:
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
v.Name = "x" + c.RandString()
|
||
|
case 2:
|
||
|
v.Type = "oauth2"
|
||
|
|
||
|
switch c.Intn(4) {
|
||
|
case 0:
|
||
|
v.Flow = "accessCode"
|
||
|
v.TokenURL = "https://" + c.RandString()
|
||
|
v.AuthorizationURL = "https://" + c.RandString()
|
||
|
case 1:
|
||
|
v.Flow = "application"
|
||
|
v.TokenURL = "https://" + c.RandString()
|
||
|
case 2:
|
||
|
v.Flow = "implicit"
|
||
|
v.AuthorizationURL = "https://" + c.RandString()
|
||
|
case 3:
|
||
|
v.Flow = "password"
|
||
|
v.TokenURL = "https://" + c.RandString()
|
||
|
default:
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
c.Fuzz(&v.Scopes)
|
||
|
default:
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
},
|
||
|
func(v *interface{}, c fuzz.Continue) {
|
||
|
*v = c.RandString() + "x"
|
||
|
},
|
||
|
func(v *string, c fuzz.Continue) {
|
||
|
*v = c.RandString() + "x"
|
||
|
},
|
||
|
func(v *ExternalDocumentation, c fuzz.Continue) {
|
||
|
v.Description = c.RandString() + "x"
|
||
|
v.URL = c.RandString() + "x"
|
||
|
},
|
||
|
func(v *SimpleSchema, c fuzz.Continue) {
|
||
|
c.FuzzNoCustom(v)
|
||
|
|
||
|
switch c.Intn(5) {
|
||
|
case 0:
|
||
|
v.Type = "string"
|
||
|
case 1:
|
||
|
v.Type = "number"
|
||
|
case 2:
|
||
|
v.Type = "boolean"
|
||
|
case 3:
|
||
|
v.Type = "integer"
|
||
|
case 4:
|
||
|
v.Type = "array"
|
||
|
default:
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
|
||
|
switch c.Intn(5) {
|
||
|
case 0:
|
||
|
v.CollectionFormat = "csv"
|
||
|
case 1:
|
||
|
v.CollectionFormat = "ssv"
|
||
|
case 2:
|
||
|
v.CollectionFormat = "tsv"
|
||
|
case 3:
|
||
|
v.CollectionFormat = "pipes"
|
||
|
case 4:
|
||
|
v.CollectionFormat = ""
|
||
|
default:
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
|
||
|
// None of the types which include SimpleSchema in our definitions
|
||
|
// actually support "example" in the official spec
|
||
|
v.Example = nil
|
||
|
|
||
|
// unsupported by openapi
|
||
|
v.Nullable = false
|
||
|
},
|
||
|
func(v *int64, c fuzz.Continue) {
|
||
|
c.Fuzz(v)
|
||
|
|
||
|
// Gnostic does not differentiate between 0 and non-specified
|
||
|
// so avoid using 0 for fuzzer
|
||
|
if *v == 0 {
|
||
|
*v = 1
|
||
|
}
|
||
|
},
|
||
|
func(v *float64, c fuzz.Continue) {
|
||
|
c.Fuzz(v)
|
||
|
|
||
|
// Gnostic does not differentiate between 0 and non-specified
|
||
|
// so avoid using 0 for fuzzer
|
||
|
if *v == 0.0 {
|
||
|
*v = 1.0
|
||
|
}
|
||
|
},
|
||
|
func(v *Parameter, c fuzz.Continue) {
|
||
|
if v == nil {
|
||
|
return
|
||
|
}
|
||
|
c.Fuzz(&v.VendorExtensible)
|
||
|
if c.RandBool() {
|
||
|
// body param
|
||
|
v.Description = c.RandString() + "x"
|
||
|
v.Name = c.RandString() + "x"
|
||
|
v.In = "body"
|
||
|
c.Fuzz(&v.Description)
|
||
|
c.Fuzz(&v.Required)
|
||
|
|
||
|
v.Schema = &Schema{}
|
||
|
c.Fuzz(&v.Schema)
|
||
|
|
||
|
} else {
|
||
|
c.Fuzz(&v.SimpleSchema)
|
||
|
c.Fuzz(&v.CommonValidations)
|
||
|
v.AllowEmptyValue = false
|
||
|
v.Description = c.RandString() + "x"
|
||
|
v.Name = c.RandString() + "x"
|
||
|
|
||
|
switch c.Intn(4) {
|
||
|
case 0:
|
||
|
// Header param
|
||
|
v.In = "header"
|
||
|
case 1:
|
||
|
// Form data param
|
||
|
v.In = "formData"
|
||
|
v.AllowEmptyValue = c.RandBool()
|
||
|
case 2:
|
||
|
// Query param
|
||
|
v.In = "query"
|
||
|
v.AllowEmptyValue = c.RandBool()
|
||
|
case 3:
|
||
|
// Path param
|
||
|
v.In = "path"
|
||
|
v.Required = true
|
||
|
default:
|
||
|
panic("unreachable")
|
||
|
}
|
||
|
|
||
|
// descendant Items of Parameter may not be refs
|
||
|
cur := v.Items
|
||
|
for cur != nil {
|
||
|
cur.Ref = Ref{}
|
||
|
cur = cur.Items
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
func(v *Schema, c fuzz.Continue) {
|
||
|
if c.RandBool() {
|
||
|
// file schema
|
||
|
c.Fuzz(&v.Default)
|
||
|
c.Fuzz(&v.Description)
|
||
|
c.Fuzz(&v.Example)
|
||
|
c.Fuzz(&v.ExternalDocs)
|
||
|
|
||
|
c.Fuzz(&v.Format)
|
||
|
c.Fuzz(&v.ReadOnly)
|
||
|
c.Fuzz(&v.Required)
|
||
|
c.Fuzz(&v.Title)
|
||
|
v.Type = StringOrArray{"file"}
|
||
|
|
||
|
} else {
|
||
|
// normal schema
|
||
|
c.Fuzz(&v.SchemaProps)
|
||
|
c.Fuzz(&v.SwaggerSchemaProps)
|
||
|
c.Fuzz(&v.VendorExtensible)
|
||
|
// c.Fuzz(&v.ExtraProps)
|
||
|
// ExtraProps will not roundtrip - gnostic throws out
|
||
|
// unrecognized keys
|
||
|
}
|
||
|
|
||
|
// Not supported by official openapi v2 spec
|
||
|
// and stripped by k8s apiserver
|
||
|
v.ID = ""
|
||
|
v.AnyOf = nil
|
||
|
v.OneOf = nil
|
||
|
v.Not = nil
|
||
|
v.Nullable = false
|
||
|
v.AdditionalItems = nil
|
||
|
v.Schema = ""
|
||
|
v.PatternProperties = nil
|
||
|
v.Definitions = nil
|
||
|
v.Dependencies = nil
|
||
|
},
|
||
|
}
|
||
|
|
||
|
var SwaggerDiffOptions = []cmp.Option{
|
||
|
// cmp.Diff panics on Ref since jsonreference.Ref uses unexported fields
|
||
|
cmp.Comparer(func(a Ref, b Ref) bool {
|
||
|
return a.String() == b.String()
|
||
|
}),
|
||
|
}
|