Skip to content

Commit 0bdb448

Browse files
committed
fix: guard nil spec fetch responses
1 parent d958d65 commit 0bdb448

3 files changed

Lines changed: 37 additions & 3 deletions

File tree

internal/spec/discover.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,9 @@ func fetchWithLinks(ctx context.Context, rawURL string, tr http.RoundTripper, fe
502502
if err != nil {
503503
return "", nil, 0, displayURL, nil, fmt.Errorf("GET %s: %w", displayURL, cleanErrorForDisplay(err, rawURL, displayURL))
504504
}
505+
if resp == nil {
506+
return "", nil, 0, displayURL, nil, fmt.Errorf("GET %s: no response", displayURL)
507+
}
505508
if resp.Body != nil {
506509
defer resp.Body.Close()
507510
}

internal/spec/discover_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,24 @@ func TestDiscoverRedactsCredentialQueryInInvalidSpecURL(t *testing.T) {
10291029
}
10301030
}
10311031

1032+
func TestDiscoverNilFetchResponseReturnsError(t *testing.T) {
1033+
cfg := DiscoverConfig{
1034+
APIName: "nil-response",
1035+
BaseURL: "https://api.example.com",
1036+
SpecURL: "https://api.example.com/openapi.json",
1037+
Fetch: func(context.Context, string, http.RoundTripper) (*http.Response, error) {
1038+
return nil, nil
1039+
},
1040+
}
1041+
_, err := Discover(context.Background(), cfg, DefaultLoaders())
1042+
if err == nil {
1043+
t.Fatal("expected nil fetch response failure")
1044+
}
1045+
if got := err.Error(); !strings.Contains(got, "GET https://api.example.com/openapi.json: no response") {
1046+
t.Fatalf("expected no response error, got: %v", err)
1047+
}
1048+
}
1049+
10321050
func TestDiscoverResolvesSameOriginRemoteExternalRefs(t *testing.T) {
10331051
root := `openapi: "3.1.0"
10341052
info:
@@ -1525,10 +1543,12 @@ func TestDiscoverRedactsCredentialQueryInExternalRefFailures(t *testing.T) {
15251543
ref string
15261544
want string
15271545
itemErr bool
1546+
itemNil bool
15281547
status int
15291548
}{
15301549
{name: "status", ref: "./item.yaml?api_key=ref-secret&version=1", want: "https://specs.example.com/item.yaml?version=1", status: http.StatusInternalServerError},
15311550
{name: "transport", ref: "./item.yaml?api_key=ref-secret&version=1", want: "https://specs.example.com/item.yaml?version=1", itemErr: true},
1551+
{name: "nil response", ref: "./item.yaml?api_key=ref-secret&version=1", want: "https://specs.example.com/item.yaml?version=1", itemNil: true},
15321552
{name: "unsupported scheme", ref: "ftp://files.example.com/item.yaml?api_key=ref-secret&version=1", want: "ftp://files.example.com/item.yaml?version=1"},
15331553
{name: "malformed", ref: "./item.yaml?api_key=ref-secret%zz&version=1", want: "https://specs.example.com/item.yaml?version=1"},
15341554
}
@@ -1548,6 +1568,9 @@ paths:
15481568
if tt.itemErr {
15491569
return nil, fmt.Errorf("dial failed for %s", r.URL.String())
15501570
}
1571+
if tt.itemNil {
1572+
return nil, nil
1573+
}
15511574
return httpResponse(tt.status, "text/plain", "server error", nil), nil
15521575
default:
15531576
return httpResponse(404, "text/plain", "not found", nil), nil
@@ -1578,6 +1601,14 @@ paths:
15781601
}
15791602
}
15801603

1604+
func TestOpenAPIRefDisplayURLPreservesAbsoluteFragmentAndRedactsQuery(t *testing.T) {
1605+
got := openAPIRefDisplayURL("https://user:pass@example.com/schema.yaml?api_key=secret&version=1#/components/schemas/Foo")
1606+
want := "https://example.com/schema.yaml?version=1#/components/schemas/Foo"
1607+
if got != want {
1608+
t.Fatalf("display URL = %q, want %q", got, want)
1609+
}
1610+
}
1611+
15811612
func TestDiscover_ExternalRefDeadlineNamesRef(t *testing.T) {
15821613
tr := roundTripperFunc(func(r *http.Request) (*http.Response, error) {
15831614
switch r.URL.Path {

internal/spec/openapi.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ func openAPIRemoteURLHandler(opts LoadOptions) func(string) (*http.Response, err
247247
if err != nil {
248248
return nil, cleanErrorForDisplay(err, rawURL, displayURL)
249249
}
250+
if resp == nil {
251+
return nil, fmt.Errorf("OpenAPI external ref %q: no response", displayURL)
252+
}
250253
if resp.Body == nil {
251254
return resp, nil
252255
}
@@ -744,9 +747,6 @@ func openAPIRefDisplayURL(raw string) string {
744747
if err != nil {
745748
return cleanPossiblyInvalidURL(raw)
746749
}
747-
if u.IsAbs() {
748-
return cleanSourceURL(raw)
749-
}
750750
u.User = nil
751751
u.RawQuery = cleanSourceURLQuery(u.Query()).Encode()
752752
return u.String()

0 commit comments

Comments
 (0)