Skip to content

Commit

Permalink
Introduce package-wide CircularReferenceCounter to work around #615 (#…
Browse files Browse the repository at this point in the history
…628)

Co-authored-by: sorintm <[email protected]>
  • Loading branch information
fenollp and sorintm authored Oct 7, 2022
1 parent ac594bc commit b31a4bb
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ jobs:
- run: git --no-pager diff --exit-code

- if: runner.os == 'Linux'
run: go test ./...
run: go test -count=10 ./...
env:
GOARCH: '386'
- run: go test ./...
- run: go test -count=10 ./...
- run: go test -count=2 -covermode=atomic ./...
- run: go test -v -run TestRaceyPatternSchema -race ./...
env:
Expand Down
33 changes: 33 additions & 0 deletions openapi3/issue615_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package openapi3_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/getkin/kin-openapi/openapi3"
)

func TestIssue615(t *testing.T) {
for {
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
_, err := loader.LoadFromFile("testdata/recursiveRef/issue615.yml")
if err == nil {
continue
}
// Test currently reproduces the issue 615: failure to load a valid spec
// Upon issue resolution, this check should be changed to require.NoError
require.Error(t, err, openapi3.CircularReferenceError)
break
}

var old int
old, openapi3.CircularReferenceCounter = openapi3.CircularReferenceCounter, 4
defer func() { openapi3.CircularReferenceCounter = old }()

loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
_, err := loader.LoadFromFile("testdata/recursiveRef/issue615.yml")
require.NoError(t, err)
}
7 changes: 4 additions & 3 deletions openapi3/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
)

var CircularReferenceError = "kin-openapi bug found: circular schema reference not handled"
var CircularReferenceCounter = 3

func foundUnresolvedRef(ref string) error {
return fmt.Errorf("found unresolved ref: %q", ref)
Expand Down Expand Up @@ -724,7 +725,7 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
}
component.Value = &schema
} else {
if visitedLimit(visited, ref, 3) {
if visitedLimit(visited, ref) {
visited = append(visited, ref)
return fmt.Errorf("%s - %s", CircularReferenceError, strings.Join(visited, " -> "))
}
Expand Down Expand Up @@ -1088,12 +1089,12 @@ func unescapeRefString(ref string) string {
return strings.Replace(strings.Replace(ref, "~1", "/", -1), "~0", "~", -1)
}

func visitedLimit(visited []string, ref string, limit int) bool {
func visitedLimit(visited []string, ref string) bool {
visitedCount := 0
for _, v := range visited {
if v == ref {
visitedCount++
if visitedCount >= limit {
if visitedCount >= CircularReferenceCounter {
return true
}
}
Expand Down
60 changes: 60 additions & 0 deletions openapi3/testdata/recursiveRef/issue615.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
openapi: "3.0.3"
info:
title: Deep recursive cyclic refs example
version: "1.0"
paths:
/foo:
$ref: ./paths/foo.yml
components:
schemas:
FilterColumnIncludes:
type: object
properties:
$includes:
$ref: '#/components/schemas/FilterPredicate'
additionalProperties: false
maxProperties: 1
minProperties: 1
FilterPredicate:
oneOf:
- $ref: '#/components/schemas/FilterValue'
- type: array
items:
$ref: '#/components/schemas/FilterPredicate'
minLength: 1
- $ref: '#/components/schemas/FilterPredicateOp'
- $ref: '#/components/schemas/FilterPredicateRangeOp'
FilterPredicateOp:
type: object
properties:
$any:
oneOf:
- type: array
items:
$ref: '#/components/schemas/FilterPredicate'
$none:
oneOf:
- $ref: '#/components/schemas/FilterPredicate'
- type: array
items:
$ref: '#/components/schemas/FilterPredicate'
additionalProperties: false
maxProperties: 1
minProperties: 1
FilterPredicateRangeOp:
type: object
properties:
$lt:
$ref: '#/components/schemas/FilterRangeValue'
additionalProperties: false
maxProperties: 2
minProperties: 2
FilterRangeValue:
oneOf:
- type: number
- type: string
FilterValue:
oneOf:
- type: number
- type: string
- type: boolean

0 comments on commit b31a4bb

Please sign in to comment.