@@ -15,6 +15,8 @@ import (
15
15
"github.com/invopop/yaml"
16
16
)
17
17
18
+ var CircularReferenceError = "kin-openapi bug found: circular schema reference not handled"
19
+
18
20
func foundUnresolvedRef (ref string ) error {
19
21
return fmt .Errorf ("found unresolved ref: %q" , ref )
20
22
}
@@ -197,7 +199,7 @@ func (loader *Loader) ResolveRefsIn(doc *T, location *url.URL) (err error) {
197
199
}
198
200
}
199
201
for _ , component := range components .Schemas {
200
- if err = loader .resolveSchemaRef (doc , component , location ); err != nil {
202
+ if err = loader .resolveSchemaRef (doc , component , location , [] string {} ); err != nil {
201
203
return
202
204
}
203
205
}
@@ -480,7 +482,7 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat
480
482
}
481
483
482
484
if schema := value .Schema ; schema != nil {
483
- if err := loader .resolveSchemaRef (doc , schema , documentPath ); err != nil {
485
+ if err := loader .resolveSchemaRef (doc , schema , documentPath , [] string {} ); err != nil {
484
486
return err
485
487
}
486
488
}
@@ -532,13 +534,13 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum
532
534
}
533
535
for _ , contentType := range value .Content {
534
536
if schema := contentType .Schema ; schema != nil {
535
- if err := loader .resolveSchemaRef (doc , schema , documentPath ); err != nil {
537
+ if err := loader .resolveSchemaRef (doc , schema , documentPath , [] string {} ); err != nil {
536
538
return err
537
539
}
538
540
}
539
541
}
540
542
if schema := value .Schema ; schema != nil {
541
- if err := loader .resolveSchemaRef (doc , schema , documentPath ); err != nil {
543
+ if err := loader .resolveSchemaRef (doc , schema , documentPath , [] string {} ); err != nil {
542
544
return err
543
545
}
544
546
}
@@ -592,7 +594,7 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d
592
594
contentType .Examples [name ] = example
593
595
}
594
596
if schema := contentType .Schema ; schema != nil {
595
- if err := loader .resolveSchemaRef (doc , schema , documentPath ); err != nil {
597
+ if err := loader .resolveSchemaRef (doc , schema , documentPath , [] string {} ); err != nil {
596
598
return err
597
599
}
598
600
}
@@ -656,7 +658,7 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen
656
658
contentType .Examples [name ] = example
657
659
}
658
660
if schema := contentType .Schema ; schema != nil {
659
- if err := loader .resolveSchemaRef (doc , schema , documentPath ); err != nil {
661
+ if err := loader .resolveSchemaRef (doc , schema , documentPath , [] string {} ); err != nil {
660
662
return err
661
663
}
662
664
contentType .Schema = schema
@@ -670,8 +672,12 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen
670
672
return nil
671
673
}
672
674
673
- func (loader * Loader ) resolveSchemaRef (doc * T , component * SchemaRef , documentPath * url.URL ) (err error ) {
674
- if component != nil && component .Value != nil {
675
+ func (loader * Loader ) resolveSchemaRef (doc * T , component * SchemaRef , documentPath * url.URL , visited []string ) (err error ) {
676
+ if component == nil {
677
+ return errors .New ("invalid schema: value MUST be an object" )
678
+ }
679
+
680
+ if component .Value != nil {
675
681
if loader .visitedSchema == nil {
676
682
loader .visitedSchema = make (map [* Schema ]struct {})
677
683
}
@@ -681,9 +687,6 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
681
687
loader .visitedSchema [component .Value ] = struct {}{}
682
688
}
683
689
684
- if component == nil {
685
- return errors .New ("invalid schema: value MUST be an object" )
686
- }
687
690
ref := component .Ref
688
691
if ref != "" {
689
692
if isSingleRefElement (ref ) {
@@ -693,12 +696,18 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
693
696
}
694
697
component .Value = & schema
695
698
} else {
699
+ if visitedLimit (visited , ref , 3 ) {
700
+ visited = append (visited , ref )
701
+ return fmt .Errorf ("%s - %s" , CircularReferenceError , strings .Join (visited , " -> " ))
702
+ }
703
+ visited = append (visited , ref )
704
+
696
705
var resolved SchemaRef
697
706
componentPath , err := loader .resolveComponent (doc , ref , documentPath , & resolved )
698
707
if err != nil {
699
708
return err
700
709
}
701
- if err := loader .resolveSchemaRef (doc , & resolved , componentPath ); err != nil {
710
+ if err := loader .resolveSchemaRef (doc , & resolved , componentPath , visited ); err != nil {
702
711
return err
703
712
}
704
713
component .Value = resolved .Value
@@ -713,37 +722,37 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
713
722
714
723
// ResolveRefs referred schemas
715
724
if v := value .Items ; v != nil {
716
- if err := loader .resolveSchemaRef (doc , v , documentPath ); err != nil {
725
+ if err := loader .resolveSchemaRef (doc , v , documentPath , visited ); err != nil {
717
726
return err
718
727
}
719
728
}
720
729
for _ , v := range value .Properties {
721
- if err := loader .resolveSchemaRef (doc , v , documentPath ); err != nil {
730
+ if err := loader .resolveSchemaRef (doc , v , documentPath , visited ); err != nil {
722
731
return err
723
732
}
724
733
}
725
734
if v := value .AdditionalProperties ; v != nil {
726
- if err := loader .resolveSchemaRef (doc , v , documentPath ); err != nil {
735
+ if err := loader .resolveSchemaRef (doc , v , documentPath , visited ); err != nil {
727
736
return err
728
737
}
729
738
}
730
739
if v := value .Not ; v != nil {
731
- if err := loader .resolveSchemaRef (doc , v , documentPath ); err != nil {
740
+ if err := loader .resolveSchemaRef (doc , v , documentPath , visited ); err != nil {
732
741
return err
733
742
}
734
743
}
735
744
for _ , v := range value .AllOf {
736
- if err := loader .resolveSchemaRef (doc , v , documentPath ); err != nil {
745
+ if err := loader .resolveSchemaRef (doc , v , documentPath , visited ); err != nil {
737
746
return err
738
747
}
739
748
}
740
749
for _ , v := range value .AnyOf {
741
- if err := loader .resolveSchemaRef (doc , v , documentPath ); err != nil {
750
+ if err := loader .resolveSchemaRef (doc , v , documentPath , visited ); err != nil {
742
751
return err
743
752
}
744
753
}
745
754
for _ , v := range value .OneOf {
746
- if err := loader .resolveSchemaRef (doc , v , documentPath ); err != nil {
755
+ if err := loader .resolveSchemaRef (doc , v , documentPath , visited ); err != nil {
747
756
return err
748
757
}
749
758
}
@@ -1046,3 +1055,16 @@ func (loader *Loader) resolvePathItemRefContinued(doc *T, pathItem *PathItem, do
1046
1055
func unescapeRefString (ref string ) string {
1047
1056
return strings .Replace (strings .Replace (ref , "~1" , "/" , - 1 ), "~0" , "~" , - 1 )
1048
1057
}
1058
+
1059
+ func visitedLimit (visited []string , ref string , limit int ) bool {
1060
+ visitedCount := 0
1061
+ for _ , v := range visited {
1062
+ if v == ref {
1063
+ visitedCount ++
1064
+ if visitedCount >= limit {
1065
+ return true
1066
+ }
1067
+ }
1068
+ }
1069
+ return false
1070
+ }
0 commit comments