Skip to content

Commit 89012b7

Browse files
Prevent infinite loop while loading openapi spec with recursive references (#310)
1 parent bdba5d1 commit 89012b7

File tree

6 files changed

+64
-2
lines changed

6 files changed

+64
-2
lines changed

openapi3/swagger_loader.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ type SwaggerLoader struct {
3434

3535
Context context.Context
3636

37-
visitedFiles map[string]struct{}
37+
visitedFiles map[string]struct{}
38+
visitedSwaggers map[string]*Swagger
3839

3940
visitedHeader map[*Header]struct{}
4041
visitedParameter map[*Parameter]struct{}
@@ -53,6 +54,7 @@ func NewSwaggerLoader() *SwaggerLoader {
5354

5455
func (swaggerLoader *SwaggerLoader) reset() {
5556
swaggerLoader.visitedFiles = make(map[string]struct{})
57+
swaggerLoader.visitedSwaggers = make(map[string]*Swagger)
5658
}
5759

5860
// LoadSwaggerFromURI loads a spec from a remote URL
@@ -170,11 +172,22 @@ func (swaggerLoader *SwaggerLoader) LoadSwaggerFromDataWithPath(data []byte, pat
170172
}
171173

172174
func (swaggerLoader *SwaggerLoader) loadSwaggerFromDataWithPathInternal(data []byte, path *url.URL) (*Swagger, error) {
175+
visited, ok := swaggerLoader.visitedSwaggers[path.String()]
176+
if ok {
177+
return visited, nil
178+
}
179+
173180
swagger := &Swagger{}
181+
swaggerLoader.visitedSwaggers[path.String()] = swagger
182+
174183
if err := yaml.Unmarshal(data, swagger); err != nil {
175184
return nil, err
176185
}
177-
return swagger, swaggerLoader.ResolveRefsIn(swagger, path)
186+
if err := swaggerLoader.ResolveRefsIn(swagger, path); err != nil {
187+
return nil, err
188+
}
189+
190+
return swagger, nil
178191
}
179192

180193
// ResolveRefsIn expands references if for instance spec was just unmarshalled
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package openapi3
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestLoaderSupportsRecursiveReference(t *testing.T) {
10+
loader := NewSwaggerLoader()
11+
loader.IsExternalRefsAllowed = true
12+
doc, err := loader.LoadSwaggerFromFile("testdata/recursiveRef/openapi.yml")
13+
require.NoError(t, err)
14+
require.NotNil(t, doc)
15+
require.NoError(t, doc.Validate(loader.Context))
16+
require.Equal(t, "bar", doc.Paths["/foo"].Get.Responses.Get(200).Value.Content.Get("application/json").Schema.Value.Properties["foo"].Value.Properties["bar"].Value.Items.Value.Example)
17+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
type: string
2+
example: bar
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type: object
2+
properties:
3+
bar:
4+
type: array
5+
items:
6+
$ref: ../openapi.yml#/components/schemas/Bar
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
openapi: "3.0.3"
2+
info:
3+
title: Recursive refs example
4+
version: "1.0"
5+
paths:
6+
/foo:
7+
$ref: ./paths/foo.yml
8+
components:
9+
schemas:
10+
Foo:
11+
$ref: ./components/Foo.yml
12+
Bar:
13+
$ref: ./components/Bar.yml
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
get:
2+
responses:
3+
"200":
4+
description: OK
5+
content:
6+
application/json:
7+
schema:
8+
type: object
9+
properties:
10+
foo:
11+
$ref: ../openapi.yml#/components/schemas/Foo

0 commit comments

Comments
 (0)