Skip to content

Commit b31a4bb

Browse files
fenollpsorintm
andauthored
Introduce package-wide CircularReferenceCounter to work around #615 (#628)
Co-authored-by: sorintm <[email protected]>
1 parent ac594bc commit b31a4bb

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

.github/workflows/go.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ jobs:
6262
- run: git --no-pager diff --exit-code
6363

6464
- if: runner.os == 'Linux'
65-
run: go test ./...
65+
run: go test -count=10 ./...
6666
env:
6767
GOARCH: '386'
68-
- run: go test ./...
68+
- run: go test -count=10 ./...
6969
- run: go test -count=2 -covermode=atomic ./...
7070
- run: go test -v -run TestRaceyPatternSchema -race ./...
7171
env:

openapi3/issue615_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package openapi3_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
8+
"github.com/getkin/kin-openapi/openapi3"
9+
)
10+
11+
func TestIssue615(t *testing.T) {
12+
for {
13+
loader := openapi3.NewLoader()
14+
loader.IsExternalRefsAllowed = true
15+
_, err := loader.LoadFromFile("testdata/recursiveRef/issue615.yml")
16+
if err == nil {
17+
continue
18+
}
19+
// Test currently reproduces the issue 615: failure to load a valid spec
20+
// Upon issue resolution, this check should be changed to require.NoError
21+
require.Error(t, err, openapi3.CircularReferenceError)
22+
break
23+
}
24+
25+
var old int
26+
old, openapi3.CircularReferenceCounter = openapi3.CircularReferenceCounter, 4
27+
defer func() { openapi3.CircularReferenceCounter = old }()
28+
29+
loader := openapi3.NewLoader()
30+
loader.IsExternalRefsAllowed = true
31+
_, err := loader.LoadFromFile("testdata/recursiveRef/issue615.yml")
32+
require.NoError(t, err)
33+
}

openapi3/loader.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
)
1818

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

2122
func foundUnresolvedRef(ref string) error {
2223
return fmt.Errorf("found unresolved ref: %q", ref)
@@ -724,7 +725,7 @@ func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPat
724725
}
725726
component.Value = &schema
726727
} else {
727-
if visitedLimit(visited, ref, 3) {
728+
if visitedLimit(visited, ref) {
728729
visited = append(visited, ref)
729730
return fmt.Errorf("%s - %s", CircularReferenceError, strings.Join(visited, " -> "))
730731
}
@@ -1088,12 +1089,12 @@ func unescapeRefString(ref string) string {
10881089
return strings.Replace(strings.Replace(ref, "~1", "/", -1), "~0", "~", -1)
10891090
}
10901091

1091-
func visitedLimit(visited []string, ref string, limit int) bool {
1092+
func visitedLimit(visited []string, ref string) bool {
10921093
visitedCount := 0
10931094
for _, v := range visited {
10941095
if v == ref {
10951096
visitedCount++
1096-
if visitedCount >= limit {
1097+
if visitedCount >= CircularReferenceCounter {
10971098
return true
10981099
}
10991100
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
openapi: "3.0.3"
2+
info:
3+
title: Deep recursive cyclic refs example
4+
version: "1.0"
5+
paths:
6+
/foo:
7+
$ref: ./paths/foo.yml
8+
components:
9+
schemas:
10+
FilterColumnIncludes:
11+
type: object
12+
properties:
13+
$includes:
14+
$ref: '#/components/schemas/FilterPredicate'
15+
additionalProperties: false
16+
maxProperties: 1
17+
minProperties: 1
18+
FilterPredicate:
19+
oneOf:
20+
- $ref: '#/components/schemas/FilterValue'
21+
- type: array
22+
items:
23+
$ref: '#/components/schemas/FilterPredicate'
24+
minLength: 1
25+
- $ref: '#/components/schemas/FilterPredicateOp'
26+
- $ref: '#/components/schemas/FilterPredicateRangeOp'
27+
FilterPredicateOp:
28+
type: object
29+
properties:
30+
$any:
31+
oneOf:
32+
- type: array
33+
items:
34+
$ref: '#/components/schemas/FilterPredicate'
35+
$none:
36+
oneOf:
37+
- $ref: '#/components/schemas/FilterPredicate'
38+
- type: array
39+
items:
40+
$ref: '#/components/schemas/FilterPredicate'
41+
additionalProperties: false
42+
maxProperties: 1
43+
minProperties: 1
44+
FilterPredicateRangeOp:
45+
type: object
46+
properties:
47+
$lt:
48+
$ref: '#/components/schemas/FilterRangeValue'
49+
additionalProperties: false
50+
maxProperties: 2
51+
minProperties: 2
52+
FilterRangeValue:
53+
oneOf:
54+
- type: number
55+
- type: string
56+
FilterValue:
57+
oneOf:
58+
- type: number
59+
- type: string
60+
- type: boolean

0 commit comments

Comments
 (0)