Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/input/formats/openapi/examples.go
2070 views
1
package openapi
2
3
import (
4
"fmt"
5
"maps"
6
"slices"
7
8
"github.com/getkin/kin-openapi/openapi3"
9
"github.com/pkg/errors"
10
)
11
12
// From: https://github.com/danielgtaylor/apisprout/blob/master/example.go
13
14
func getSchemaExample(schema *openapi3.Schema) (interface{}, bool) {
15
if schema.Example != nil {
16
return schema.Example, true
17
}
18
19
if schema.Default != nil {
20
return schema.Default, true
21
}
22
23
if len(schema.Enum) > 0 {
24
return schema.Enum[0], true
25
}
26
return nil, false
27
}
28
29
// stringFormatExample returns an example string based on the given format.
30
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.3
31
func stringFormatExample(format string) string {
32
switch format {
33
case "date":
34
// https://tools.ietf.org/html/rfc3339
35
return "2018-07-23"
36
case "date-time":
37
// This is the date/time of API Sprout's first commit! :-)
38
return "2018-07-23T22:58:00-07:00"
39
case "time":
40
return "22:58:00-07:00"
41
case "email":
42
return "[email protected]"
43
case "hostname":
44
// https://tools.ietf.org/html/rfc2606#page-2
45
return "example.com"
46
case "ipv4":
47
// https://tools.ietf.org/html/rfc5737
48
return "198.51.100.0"
49
case "ipv6":
50
// https://tools.ietf.org/html/rfc3849
51
return "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
52
case "uri":
53
return "https://tools.ietf.org/html/rfc3986"
54
case "uri-template":
55
// https://tools.ietf.org/html/rfc6570
56
return "http://example.com/dictionary/{term:1}/{term}"
57
case "json-pointer":
58
// https://tools.ietf.org/html/rfc6901
59
return "#/components/parameters/term"
60
case "regex":
61
// https://stackoverflow.com/q/3296050/164268
62
return "/^1?$|^(11+?)\\1+$/"
63
case "uuid":
64
// https://www.ietf.org/rfc/rfc4122.txt
65
return "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
66
case "password":
67
return "********"
68
case "binary":
69
return "sagefuzzertest"
70
}
71
return ""
72
}
73
74
// excludeFromMode will exclude a schema if the mode is request and the schema
75
// is read-only
76
func excludeFromMode(schema *openapi3.Schema) bool {
77
if schema == nil {
78
return true
79
}
80
81
if schema.ReadOnly {
82
return true
83
}
84
return false
85
}
86
87
// isRequired checks whether a key is actually required.
88
func isRequired(schema *openapi3.Schema, key string) bool {
89
return slices.Contains(schema.Required, key)
90
}
91
92
type cachedSchema struct {
93
pending bool
94
out interface{}
95
}
96
97
var (
98
// ErrRecursive is when a schema is impossible to represent because it infinitely recurses.
99
ErrRecursive = errors.New("Recursive schema")
100
101
// ErrNoExample is sent when no example was found for an operation.
102
ErrNoExample = errors.New("No example found")
103
)
104
105
func openAPIExample(schema *openapi3.Schema, cache map[*openapi3.Schema]*cachedSchema) (out interface{}, err error) {
106
if ex, ok := getSchemaExample(schema); ok {
107
return ex, nil
108
}
109
110
cached, ok := cache[schema]
111
if !ok {
112
cached = &cachedSchema{
113
pending: true,
114
}
115
cache[schema] = cached
116
} else if cached.pending {
117
return nil, ErrRecursive
118
} else {
119
return cached.out, nil
120
}
121
122
defer func() {
123
cached.pending = false
124
cached.out = out
125
}()
126
127
// Handle combining keywords
128
if len(schema.OneOf) > 0 {
129
var ex interface{}
130
var err error
131
132
for _, candidate := range schema.OneOf {
133
ex, err = openAPIExample(candidate.Value, cache)
134
if err == nil {
135
break
136
}
137
}
138
return ex, err
139
}
140
if len(schema.AnyOf) > 0 {
141
var ex interface{}
142
var err error
143
144
for _, candidate := range schema.AnyOf {
145
ex, err = openAPIExample(candidate.Value, cache)
146
if err == nil {
147
break
148
}
149
}
150
return ex, err
151
}
152
if len(schema.AllOf) > 0 {
153
example := map[string]interface{}{}
154
155
for _, allOf := range schema.AllOf {
156
candidate, err := openAPIExample(allOf.Value, cache)
157
if err != nil {
158
return nil, err
159
}
160
161
value, ok := candidate.(map[string]interface{})
162
if !ok {
163
return nil, ErrNoExample
164
}
165
166
maps.Copy(example, value)
167
}
168
return example, nil
169
}
170
171
switch {
172
case schema.Type.Is("boolean"):
173
return true, nil
174
case schema.Type.Is("number"), schema.Type.Is("integer"):
175
value := 0.0
176
177
if schema.Min != nil && *schema.Min > value {
178
value = *schema.Min
179
if schema.ExclusiveMin {
180
if schema.Max != nil {
181
// Make the value half way.
182
value = (*schema.Min + *schema.Max) / 2.0
183
} else {
184
value++
185
}
186
}
187
}
188
189
if schema.Max != nil && *schema.Max < value {
190
value = *schema.Max
191
if schema.ExclusiveMax {
192
if schema.Min != nil {
193
// Make the value half way.
194
value = (*schema.Min + *schema.Max) / 2.0
195
} else {
196
value--
197
}
198
}
199
}
200
201
if schema.MultipleOf != nil && int(value)%int(*schema.MultipleOf) != 0 {
202
value += float64(int(*schema.MultipleOf) - (int(value) % int(*schema.MultipleOf)))
203
}
204
205
if schema.Type.Is("integer") {
206
return int(value), nil
207
}
208
return value, nil
209
case schema.Type.Is("string"):
210
if ex := stringFormatExample(schema.Format); ex != "" {
211
return ex, nil
212
}
213
example := "string"
214
215
for schema.MinLength > uint64(len(example)) {
216
example += example
217
}
218
219
if schema.MaxLength != nil && *schema.MaxLength < uint64(len(example)) {
220
example = example[:*schema.MaxLength]
221
}
222
return example, nil
223
case schema.Type.Is("array"), schema.Items != nil:
224
example := []interface{}{}
225
226
if schema.Items != nil && schema.Items.Value != nil {
227
ex, err := openAPIExample(schema.Items.Value, cache)
228
if err != nil {
229
return nil, fmt.Errorf("can't get example for array item: %+v", err)
230
}
231
232
example = append(example, ex)
233
234
for uint64(len(example)) < schema.MinItems {
235
example = append(example, ex)
236
}
237
}
238
return example, nil
239
case schema.Type.Is("object"), len(schema.Properties) > 0:
240
example := map[string]interface{}{}
241
242
for k, v := range schema.Properties {
243
if excludeFromMode(v.Value) {
244
continue
245
}
246
247
ex, err := openAPIExample(v.Value, cache)
248
if err == ErrRecursive {
249
if isRequired(schema, k) {
250
return nil, fmt.Errorf("can't get example for '%s': %+v", k, err)
251
}
252
} else if err != nil {
253
return nil, fmt.Errorf("can't get example for '%s': %+v", k, err)
254
} else {
255
example[k] = ex
256
}
257
}
258
259
if schema.AdditionalProperties.Has != nil && schema.AdditionalProperties.Schema != nil {
260
addl := schema.AdditionalProperties.Schema.Value
261
262
if !excludeFromMode(addl) {
263
ex, err := openAPIExample(addl, cache)
264
if err == ErrRecursive {
265
// We just won't add this if it's recursive.
266
} else if err != nil {
267
return nil, fmt.Errorf("can't get example for additional properties: %+v", err)
268
} else {
269
example["additionalPropertyName"] = ex
270
}
271
}
272
}
273
return example, nil
274
}
275
return nil, ErrNoExample
276
}
277
278
// generateExampleFromSchema creates an example structure from an OpenAPI 3 schema
279
// object, which is an extended subset of JSON Schema.
280
//
281
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#schemaObject
282
func generateExampleFromSchema(schema *openapi3.Schema) (interface{}, error) {
283
return openAPIExample(schema, make(map[*openapi3.Schema]*cachedSchema)) // TODO: Use caching
284
}
285
286
func generateEmptySchemaValue(contentType string) *openapi3.Schema {
287
schema := &openapi3.Schema{}
288
objectType := &openapi3.Types{"object"}
289
stringType := &openapi3.Types{"string"}
290
291
switch contentType {
292
case "application/json":
293
schema.Type = objectType
294
schema.Properties = make(map[string]*openapi3.SchemaRef)
295
case "application/xml":
296
schema.Type = stringType
297
schema.Format = "xml"
298
schema.Example = "<?xml version=\"1.0\"?><root/>"
299
case "text/plain":
300
schema.Type = stringType
301
case "application/x-www-form-urlencoded":
302
schema.Type = objectType
303
schema.Properties = make(map[string]*openapi3.SchemaRef)
304
case "multipart/form-data":
305
schema.Type = objectType
306
schema.Properties = make(map[string]*openapi3.SchemaRef)
307
case "application/octet-stream":
308
default:
309
schema.Type = stringType
310
schema.Format = "binary"
311
}
312
313
return schema
314
}
315
316