Skip to content

Commit 5217252

Browse files
authored
handle the main ref dereferencing issues (#229)
Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com>
1 parent 26e9a37 commit 5217252

2 files changed

Lines changed: 140 additions & 17 deletions

File tree

openapi3/refs_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package openapi3
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestIssue222(t *testing.T) {
10+
spec := `
11+
openapi: 3.0.0
12+
info:
13+
version: 1.0.0
14+
title: Swagger Petstore
15+
license:
16+
name: MIT
17+
servers:
18+
- url: 'http://petstore.swagger.io/v1'
19+
paths:
20+
/pets:
21+
get:
22+
summary: List all pets
23+
operationId: listPets
24+
tags:
25+
- pets
26+
parameters:
27+
- name: limit
28+
in: query
29+
description: How many items to return at one time (max 100)
30+
required: false
31+
schema:
32+
type: integer
33+
format: int32
34+
responses:
35+
'200': # <--------------- PANIC HERE
36+
37+
post:
38+
summary: Create a pet
39+
operationId: createPets
40+
tags:
41+
- pets
42+
responses:
43+
'201':
44+
description: Null response
45+
default:
46+
description: unexpected error
47+
content:
48+
application/json:
49+
schema:
50+
$ref: '#/components/schemas/Error'
51+
'/pets/{petId}':
52+
get:
53+
summary: Info for a specific pet
54+
operationId: showPetById
55+
tags:
56+
- pets
57+
parameters:
58+
- name: petId
59+
in: path
60+
required: true
61+
description: The id of the pet to retrieve
62+
schema:
63+
type: string
64+
responses:
65+
'200':
66+
description: Expected response to a valid request
67+
content:
68+
application/json:
69+
schema:
70+
$ref: '#/components/schemas/Pet'
71+
default:
72+
description: unexpected error
73+
content:
74+
application/json:
75+
schema:
76+
$ref: '#/components/schemas/Error'
77+
components:
78+
schemas:
79+
Pet:
80+
type: object
81+
required:
82+
- id
83+
- name
84+
properties:
85+
id:
86+
type: integer
87+
format: int64
88+
name:
89+
type: string
90+
tag:
91+
type: string
92+
Pets:
93+
type: array
94+
items:
95+
$ref: '#/components/schemas/Pet'
96+
Error:
97+
type: object
98+
required:
99+
- code
100+
- message
101+
properties:
102+
code:
103+
type: integer
104+
format: int32
105+
message:
106+
type: string
107+
`
108+
109+
_, err := NewSwaggerLoader().LoadSwaggerFromData([]byte(spec))
110+
require.EqualError(t, err, `invalid response: value MUST be a JSON object`)
111+
}

openapi3/swagger_loader.go

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,16 @@ func (swaggerLoader *SwaggerLoader) resolveRefSwagger(swagger *Swagger, ref stri
355355
}
356356

357357
func (swaggerLoader *SwaggerLoader) resolveHeaderRef(swagger *Swagger, component *HeaderRef, path *url.URL) error {
358-
// Prevent infinite recursion
359358
visited := swaggerLoader.visited
360359
if _, isVisited := visited[component]; isVisited {
361360
return nil
362361
}
363362
visited[component] = struct{}{}
364363

365-
// Resolve ref
366364
const prefix = "#/components/headers/"
365+
if component == nil {
366+
return errors.New("invalid header: value MUST be a JSON object")
367+
}
367368
if ref := component.Ref; len(ref) > 0 {
368369
if isSingleRefElement(ref) {
369370
var header Header
@@ -400,15 +401,16 @@ func (swaggerLoader *SwaggerLoader) resolveHeaderRef(swagger *Swagger, component
400401
}
401402

402403
func (swaggerLoader *SwaggerLoader) resolveParameterRef(swagger *Swagger, component *ParameterRef, documentPath *url.URL) error {
403-
// Prevent infinite recursion
404404
visited := swaggerLoader.visited
405405
if _, isVisited := visited[component]; isVisited {
406406
return nil
407407
}
408408
visited[component] = struct{}{}
409409

410-
// Resolve ref
411410
const prefix = "#/components/parameters/"
411+
if component == nil {
412+
return errors.New("invalid parameter: value MUST be a JSON object")
413+
}
412414
ref := component.Ref
413415
if len(ref) > 0 {
414416
if isSingleRefElement(ref) {
@@ -461,15 +463,16 @@ func (swaggerLoader *SwaggerLoader) resolveParameterRef(swagger *Swagger, compon
461463
}
462464

463465
func (swaggerLoader *SwaggerLoader) resolveRequestBodyRef(swagger *Swagger, component *RequestBodyRef, path *url.URL) error {
464-
// Prevent infinite recursion
465466
visited := swaggerLoader.visited
466467
if _, isVisited := visited[component]; isVisited {
467468
return nil
468469
}
469470
visited[component] = struct{}{}
470471

471-
// Resolve ref
472472
const prefix = "#/components/requestBodies/"
473+
if component == nil {
474+
return errors.New("invalid requestBody: value MUST be a JSON object")
475+
}
473476
if ref := component.Ref; len(ref) > 0 {
474477
if isSingleRefElement(ref) {
475478
var requestBody RequestBody
@@ -514,16 +517,17 @@ func (swaggerLoader *SwaggerLoader) resolveRequestBodyRef(swagger *Swagger, comp
514517
}
515518

516519
func (swaggerLoader *SwaggerLoader) resolveResponseRef(swagger *Swagger, component *ResponseRef, documentPath *url.URL) error {
517-
// Prevent infinite recursion
518520
visited := swaggerLoader.visited
519521
if _, isVisited := visited[component]; isVisited {
520522
return nil
521523
}
522524
visited[component] = struct{}{}
523525

524-
// Resolve ref
525-
ref := component.Ref
526526
const prefix = "#/components/responses/"
527+
if component == nil {
528+
return errors.New("invalid response: value MUST be a JSON object")
529+
}
530+
ref := component.Ref
527531
if len(ref) > 0 {
528532

529533
if isSingleRefElement(ref) {
@@ -588,15 +592,16 @@ func (swaggerLoader *SwaggerLoader) resolveResponseRef(swagger *Swagger, compone
588592
}
589593

590594
func (swaggerLoader *SwaggerLoader) resolveSchemaRef(swagger *Swagger, component *SchemaRef, documentPath *url.URL) error {
591-
// Prevent infinite recursion
592595
visited := swaggerLoader.visited
593596
if _, isVisited := visited[component]; isVisited {
594597
return nil
595598
}
596599
visited[component] = struct{}{}
597600

598-
// Resolve ref
599601
const prefix = "#/components/schemas/"
602+
if component == nil {
603+
return errors.New("invalid schema: value MUST be a JSON object")
604+
}
600605
ref := component.Ref
601606
if len(ref) > 0 {
602607
if isSingleRefElement(ref) {
@@ -673,15 +678,16 @@ func (swaggerLoader *SwaggerLoader) resolveSchemaRef(swagger *Swagger, component
673678
}
674679

675680
func (swaggerLoader *SwaggerLoader) resolveSecuritySchemeRef(swagger *Swagger, component *SecuritySchemeRef, path *url.URL) error {
676-
// Prevent infinite recursion
677681
visited := swaggerLoader.visited
678682
if _, isVisited := visited[component]; isVisited {
679683
return nil
680684
}
681685
visited[component] = struct{}{}
682686

683-
// Resolve ref
684687
const prefix = "#/components/securitySchemes/"
688+
if component == nil {
689+
return errors.New("invalid securityScheme: value MUST be a JSON object")
690+
}
685691
if ref := component.Ref; len(ref) > 0 {
686692
if isSingleRefElement(ref) {
687693
var scheme SecurityScheme
@@ -709,14 +715,16 @@ func (swaggerLoader *SwaggerLoader) resolveSecuritySchemeRef(swagger *Swagger, c
709715
}
710716

711717
func (swaggerLoader *SwaggerLoader) resolveExampleRef(swagger *Swagger, component *ExampleRef, path *url.URL) error {
712-
// Prevent infinite recursion
713718
visited := swaggerLoader.visited
714719
if _, isVisited := visited[component]; isVisited {
715720
return nil
716721
}
717722
visited[component] = struct{}{}
718723

719724
const prefix = "#/components/examples/"
725+
if component == nil {
726+
return errors.New("invalid example: value MUST be a JSON object")
727+
}
720728
if ref := component.Ref; len(ref) > 0 {
721729
if isSingleRefElement(ref) {
722730
var example Example
@@ -744,14 +752,16 @@ func (swaggerLoader *SwaggerLoader) resolveExampleRef(swagger *Swagger, componen
744752
}
745753

746754
func (swaggerLoader *SwaggerLoader) resolveLinkRef(swagger *Swagger, component *LinkRef, path *url.URL) error {
747-
// Prevent infinite recursion
748755
visited := swaggerLoader.visited
749756
if _, isVisited := visited[component]; isVisited {
750757
return nil
751758
}
752759
visited[component] = struct{}{}
753760

754761
const prefix = "#/components/links/"
762+
if component == nil {
763+
return errors.New("invalid link: value MUST be a JSON object")
764+
}
755765
if ref := component.Ref; len(ref) > 0 {
756766
if isSingleRefElement(ref) {
757767
var link Link
@@ -779,7 +789,6 @@ func (swaggerLoader *SwaggerLoader) resolveLinkRef(swagger *Swagger, component *
779789
}
780790

781791
func (swaggerLoader *SwaggerLoader) resolvePathItemRef(swagger *Swagger, entrypoint string, pathItem *PathItem, documentPath *url.URL) (err error) {
782-
// Prevent infinite recursion
783792
visited := swaggerLoader.visitedFiles
784793
key := "_"
785794
if documentPath != nil {
@@ -791,6 +800,10 @@ func (swaggerLoader *SwaggerLoader) resolvePathItemRef(swagger *Swagger, entrypo
791800
}
792801
visited[key] = struct{}{}
793802

803+
const prefix = "#/paths/"
804+
if pathItem == nil {
805+
return errors.New("invalid path item: value MUST be a JSON object")
806+
}
794807
ref := pathItem.Ref
795808
if ref != "" {
796809
if isSingleRefElement(ref) {
@@ -804,7 +817,6 @@ func (swaggerLoader *SwaggerLoader) resolvePathItemRef(swagger *Swagger, entrypo
804817
return
805818
}
806819

807-
prefix := "#/paths/"
808820
if !strings.HasPrefix(ref, prefix) {
809821
err = fmt.Errorf("expected prefix '%s' in URI '%s'", prefix, ref)
810822
return

0 commit comments

Comments
 (0)