mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Added a safety check to resolver
If the number of relatives exceeds 500 deep when resolving, libopenapi will now log a warning and escape that path from continuing. There is no reason on earth for a depth this large. It most likely indicates a circular reference that was ignored and then resolved. Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
@@ -310,7 +310,7 @@ func (resolver *Resolver) VisitReference(ref *Reference, seen map[string]bool, j
|
||||
}
|
||||
|
||||
journey = append(journey, ref)
|
||||
relatives := resolver.extractRelatives(ref, ref.Node, nil, seen, journey, resolve)
|
||||
relatives := resolver.extractRelatives(ref, ref.Node, nil, seen, journey, resolve, 0)
|
||||
|
||||
seen = make(map[string]bool)
|
||||
|
||||
@@ -421,19 +421,33 @@ func (resolver *Resolver) isInfiniteCircularDependency(ref *Reference, visitedDe
|
||||
|
||||
func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.Node,
|
||||
foundRelatives map[string]bool,
|
||||
journey []*Reference, resolve bool) []*Reference {
|
||||
journey []*Reference, resolve bool, depth int) []*Reference {
|
||||
|
||||
if len(journey) > 100 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// this is a safety check to prevent a stack overflow.
|
||||
if depth > 500 {
|
||||
def := "unknown"
|
||||
if ref != nil {
|
||||
def = ref.FullDefinition
|
||||
}
|
||||
if resolver.specIndex != nil && resolver.specIndex.logger != nil {
|
||||
resolver.specIndex.logger.Warn("libopenapi resolver: relative depth exceeded 500 levels, "+
|
||||
"check for circular references - resolving may be incomplete",
|
||||
"reference", def)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var found []*Reference
|
||||
|
||||
if len(node.Content) > 0 {
|
||||
for i, n := range node.Content {
|
||||
if utils.IsNodeMap(n) || utils.IsNodeArray(n) {
|
||||
|
||||
found = append(found, resolver.extractRelatives(ref, n, node, foundRelatives, journey, resolve)...)
|
||||
depth++
|
||||
found = append(found, resolver.extractRelatives(ref, n, node, foundRelatives, journey, resolve, depth)...)
|
||||
}
|
||||
|
||||
if i%2 == 0 && n.Value == "$ref" {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -432,7 +434,43 @@ func TestResolver_DeepJourney(t *testing.T) {
|
||||
}
|
||||
idx := NewSpecIndexWithConfig(nil, CreateClosedAPIIndexConfig())
|
||||
resolver := NewResolver(idx)
|
||||
assert.Nil(t, resolver.extractRelatives(nil, nil, nil, nil, journey, false))
|
||||
assert.Nil(t, resolver.extractRelatives(nil, nil, nil, nil, journey, false, 0))
|
||||
}
|
||||
|
||||
func TestResolver_DeepDepth(t *testing.T) {
|
||||
var refA, refB *yaml.Node
|
||||
|
||||
refA = &yaml.Node{
|
||||
Value: "A",
|
||||
Tag: "!!seq",
|
||||
}
|
||||
|
||||
refB = &yaml.Node{
|
||||
Value: "B",
|
||||
Tag: "!!seq",
|
||||
}
|
||||
|
||||
refA.Content = append(refA.Content, refB)
|
||||
refB.Content = append(refB.Content, refA)
|
||||
|
||||
idx := NewSpecIndexWithConfig(nil, CreateClosedAPIIndexConfig())
|
||||
resolver := NewResolver(idx)
|
||||
|
||||
// add a logger
|
||||
var log []byte
|
||||
buf := bytes.NewBuffer(log)
|
||||
logger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
idx.logger = logger
|
||||
|
||||
ref := &Reference{
|
||||
FullDefinition: "#/components/schemas/A",
|
||||
}
|
||||
found := resolver.extractRelatives(ref, refA, nil, nil, nil, false, 0)
|
||||
|
||||
assert.Nil(t, found)
|
||||
assert.Contains(t, buf.String(), "libopenapi resolver: relative depth exceeded 500 levels")
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_Stripe_NoRolodex(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user