@@ -15,6 +15,8 @@ import (
1515 "github.com/invopop/yaml"
1616)
1717
18+ var CircularReferenceError = "kin-openapi bug found: circular schema reference not handled"
19+
1820func foundUnresolvedRef (ref string ) error {
1921 return fmt .Errorf ("found unresolved ref: %q" , ref )
2022}
@@ -197,7 +199,7 @@ func (loader *Loader) ResolveRefsIn(doc *T, location *url.URL) (err error) {
197199 }
198200 }
199201 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 {
201203 return
202204 }
203205 }
@@ -480,7 +482,7 @@ func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPat
480482 }
481483
482484 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 {
484486 return err
485487 }
486488 }
@@ -532,13 +534,13 @@ func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, docum
532534 }
533535 for _ , contentType := range value .Content {
534536 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 {
536538 return err
537539 }
538540 }
539541 }
540542 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 {
542544 return err
543545 }
544546 }
@@ -592,7 +594,7 @@ func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, d
592594 contentType .Examples [name ] = example
593595 }
594596 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 {
596598 return err
597599 }
598600 }
@@ -656,7 +658,7 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen
656658 contentType .Examples [name ] = example
657659 }
658660 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 {
660662 return err
661663 }
662664 contentType .Schema = schema
@@ -670,8 +672,12 @@ func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documen
670672 return nil
671673}
672674
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 {
675681 if loader .visitedSchema == nil {
676682 loader .visitedSchema = make (map [* Schema ]struct {})
677683 }
@@ -681,9 +687,6 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
681687 loader .visitedSchema [component .Value ] = struct {}{}
682688 }
683689
684- if component == nil {
685- return errors .New ("invalid schema: value MUST be an object" )
686- }
687690 ref := component .Ref
688691 if ref != "" {
689692 if isSingleRefElement (ref ) {
@@ -693,12 +696,18 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
693696 }
694697 component .Value = & schema
695698 } 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+
696705 var resolved SchemaRef
697706 componentPath , err := loader .resolveComponent (doc , ref , documentPath , & resolved )
698707 if err != nil {
699708 return err
700709 }
701- if err := loader .resolveSchemaRef (doc , & resolved , componentPath ); err != nil {
710+ if err := loader .resolveSchemaRef (doc , & resolved , componentPath , visited ); err != nil {
702711 return err
703712 }
704713 component .Value = resolved .Value
@@ -713,37 +722,37 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
713722
714723 // ResolveRefs referred schemas
715724 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 {
717726 return err
718727 }
719728 }
720729 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 {
722731 return err
723732 }
724733 }
725734 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 {
727736 return err
728737 }
729738 }
730739 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 {
732741 return err
733742 }
734743 }
735744 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 {
737746 return err
738747 }
739748 }
740749 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 {
742751 return err
743752 }
744753 }
745754 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 {
747756 return err
748757 }
749758 }
@@ -1046,3 +1055,16 @@ func (loader *Loader) resolvePathItemRefContinued(doc *T, pathItem *PathItem, do
10461055func unescapeRefString (ref string ) string {
10471056 return strings .Replace (strings .Replace (ref , "~1" , "/" , - 1 ), "~0" , "~" , - 1 )
10481057}
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