Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 993c5c3

Browse files
authoredDec 1, 2024··
Merge pull request #229 from cerberauth/fix-recursive-openapi-params
Limit the depth when openapi params reference itself
2 parents a5b3361 + 9be6691 commit 993c5c3

File tree

5 files changed

+184
-7
lines changed

5 files changed

+184
-7
lines changed
 

‎.github/workflows/scans.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ jobs:
287287
"simple_api_key.openapi.json",
288288
"simple_http_bearer_jwt.openapi.json",
289289
"simple_http_bearer.openapi.json",
290+
"complex.openapi.json",
290291
]
291292

292293
steps:

‎openapi/operation.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ func GetOperationPath(p string, params openapi3.Parameters) (string, error) {
4141
if v.Value.In != "path" {
4242
continue
4343
}
44-
45-
subs[v.Value.Name] = getSchemaValue(v.Value.Schema.Value)
44+
subs[v.Value.Name] = getSchemaValue(v.Value.Schema.Value, 0)
4645
}
4746

4847
return stduritemplate.Expand(p, subs)

‎openapi/param.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import (
88
"github.com/getkin/kin-openapi/openapi3"
99
)
1010

11+
const maximumDepth = 4
12+
1113
func getParameterValue(param *openapi3.Parameter) string {
1214
if param.Schema != nil {
13-
value := getSchemaValue(param.Schema.Value)
15+
value := getSchemaValue(param.Schema.Value, 0)
1416
switch {
1517
case param.Schema.Value.Type.Is("string"):
1618
return value.(string)
@@ -66,7 +68,7 @@ func getRequestBodyValue(requestBody *openapi3.RequestBody) (*bytes.Buffer, stri
6668
if requestBody.Content != nil {
6769
for mediaType, mediaTypeValue := range requestBody.Content {
6870
if mediaTypeValue.Schema != nil {
69-
body := getSchemaValue(mediaTypeValue.Schema.Value)
71+
body := getSchemaValue(mediaTypeValue.Schema.Value, 0)
7072
switch mediaType {
7173
case "application/json":
7274
return mapRequestBodyFakeValueToJSON(mediaTypeValue.Schema.Value, body), "application/json"
@@ -80,7 +82,7 @@ func getRequestBodyValue(requestBody *openapi3.RequestBody) (*bytes.Buffer, stri
8082
return bytes.NewBuffer(nil), ""
8183
}
8284

83-
func getSchemaValue(schema *openapi3.Schema) interface{} {
85+
func getSchemaValue(schema *openapi3.Schema, depth int) interface{} {
8486
if schema.Example != nil {
8587
return schema.Example
8688
} else if len(schema.Enum) > 0 {
@@ -94,11 +96,17 @@ func getSchemaValue(schema *openapi3.Schema) interface{} {
9496
case schema.Type.Is("boolean"):
9597
return gofakeit.Bool()
9698
case schema.Type.Is("array"):
97-
return []interface{}{getSchemaValue(schema.Items.Value)}
99+
if depth > maximumDepth {
100+
return []interface{}{}
101+
}
102+
return []interface{}{getSchemaValue(schema.Items.Value, depth+1)}
98103
case schema.Type.Is("object"):
99104
object := map[string]interface{}{}
105+
if depth > maximumDepth {
106+
return object
107+
}
100108
for key, value := range schema.Properties {
101-
object[key] = getSchemaValue(value.Value)
109+
object[key] = getSchemaValue(value.Value, depth+1)
102110
}
103111
return object
104112
case schema.Type.Is("string"):

‎openapi/param_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,17 @@ func TestGetSchemaValue_WhenRequestBodyParametersWithObjectExampleAndArrayExampl
265265
assert.NotNil(t, operations[0].Body)
266266
assert.Equal(t, expected, operations[0].Body)
267267
}
268+
269+
func TestRecursiveParameters(t *testing.T) {
270+
openapiContract, operr := openapi.LoadFromData(
271+
context.Background(),
272+
[]byte(`{"openapi":"3.0.2","servers":[{"url":"http://localhost:8080"}],"paths":{"/":{"post":{"summary":"Create an item","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}},"responses":{"201":{"description":"Item created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Item"}}}}}}}},"components":{"schemas":{"Item":{"type":"object","properties":{"details":{"type":"object","properties":{"description":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/components/schemas/Attribute"}}}}}},"Attribute":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"},"subAttributes":{"type":"array","items":{"$ref":"#/components/schemas/Attribute"}}}}}}}}`),
273+
)
274+
275+
securitySchemesMap, _ := openapiContract.SecuritySchemeMap(openapi.NewEmptySecuritySchemeValues())
276+
operations, err := openapiContract.Operations(nil, securitySchemesMap)
277+
278+
assert.NoError(t, operr)
279+
assert.NoError(t, err)
280+
assert.NotNil(t, operations[0].Body)
281+
}

‎test/stub/complex.openapi.json

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
{
2+
"openapi": "3.0.2",
3+
"info": {
4+
"title": "Complex API",
5+
"description": "API with complex and recursive parameters",
6+
"version": "1.0.0"
7+
},
8+
"servers": [
9+
{
10+
"url": "http://localhost:8080"
11+
}
12+
],
13+
"paths": {
14+
"/": {
15+
"get": {
16+
"summary": "Get items",
17+
"parameters": [
18+
{
19+
"name": "filter",
20+
"in": "query",
21+
"required": false,
22+
"schema": {
23+
"type": "object",
24+
"properties": {
25+
"name": {
26+
"type": "string"
27+
},
28+
"tags": {
29+
"type": "array",
30+
"items": {
31+
"type": "string"
32+
}
33+
}
34+
}
35+
}
36+
}
37+
],
38+
"responses": {
39+
"200": {
40+
"description": "successful operation",
41+
"content": {
42+
"application/json": {
43+
"schema": {
44+
"type": "array",
45+
"items": {
46+
"$ref": "#/components/schemas/Item"
47+
}
48+
}
49+
}
50+
}
51+
}
52+
},
53+
"security": [
54+
{
55+
"bearer_auth": []
56+
},
57+
{
58+
"api_key_auth": []
59+
}
60+
]
61+
},
62+
"post": {
63+
"summary": "Create an item",
64+
"requestBody": {
65+
"required": true,
66+
"content": {
67+
"application/json": {
68+
"schema": {
69+
"$ref": "#/components/schemas/Item"
70+
}
71+
}
72+
}
73+
},
74+
"responses": {
75+
"201": {
76+
"description": "Item created",
77+
"content": {
78+
"application/json": {
79+
"schema": {
80+
"$ref": "#/components/schemas/Item"
81+
}
82+
}
83+
}
84+
}
85+
},
86+
"security": [
87+
{
88+
"bearer_auth": []
89+
},
90+
{
91+
"api_key_auth": []
92+
}
93+
]
94+
}
95+
}
96+
},
97+
"components": {
98+
"securitySchemes": {
99+
"bearer_auth": {
100+
"type": "http",
101+
"scheme": "bearer",
102+
"bearerFormat": "JWT"
103+
},
104+
"api_key_auth": {
105+
"type": "apiKey",
106+
"in": "header",
107+
"name": "X-API-Key"
108+
}
109+
},
110+
"schemas": {
111+
"Item": {
112+
"type": "object",
113+
"properties": {
114+
"id": {
115+
"type": "string"
116+
},
117+
"name": {
118+
"type": "string"
119+
},
120+
"details": {
121+
"type": "object",
122+
"properties": {
123+
"description": {
124+
"type": "string"
125+
},
126+
"attributes": {
127+
"type": "array",
128+
"items": {
129+
"$ref": "#/components/schemas/Attribute"
130+
}
131+
}
132+
}
133+
}
134+
}
135+
},
136+
"Attribute": {
137+
"type": "object",
138+
"properties": {
139+
"key": {
140+
"type": "string"
141+
},
142+
"value": {
143+
"type": "string"
144+
},
145+
"subAttributes": {
146+
"type": "array",
147+
"items": {
148+
"$ref": "#/components/schemas/Attribute"
149+
}
150+
}
151+
}
152+
}
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)
Please sign in to comment.