From 44d0f78ec1e8f7afd7d9a2f0381dd1e3ea5962ac Mon Sep 17 00:00:00 2001 From: Prem Kumar Kalle Date: Thu, 6 Aug 2026 15:05:21 -0700 Subject: [PATCH] Fix(curl): surface auth errors and avoid panic on nil HTTP response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MakeCurlRequest assumed that a non-nil error always came with an HTTP response (e.g. a 4xx/5xx from the API). That assumption breaks when the request fails before reaching the API — for example a token refresh / client-credentials authentication failure in the UAA request wrapper, which returns an error with a nil *http.Response. In that case: - Without --fail, the error was swallowed and cf curl printed empty output, giving the user no indication anything went wrong. - With --fail, the code dereferenced httpResponse.StatusCode on the nil response and panicked. Only surface CurlExit22Error when an HTTP response is actually present; when the response is nil, return the underlying error directly so the user sees a real message (e.g. "Bad credentials") and cf curl exits cleanly instead of panicking. Existing behavior for real HTTP error responses (print body by default, fail only with --fail) is unchanged. Signed-off-by: Prem Kumar Kalle --- actor/v7action/curl.go | 15 +++++++++++++-- actor/v7action/curl_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/actor/v7action/curl.go b/actor/v7action/curl.go index cd388025105..caa60f3cd88 100644 --- a/actor/v7action/curl.go +++ b/actor/v7action/curl.go @@ -47,8 +47,19 @@ func (actor Actor) MakeCurlRequest( requestBodyBytes, ) - if err != nil && failOnHTTPError { - return nil, nil, translatableerror.CurlExit22Error{StatusCode: httpResponse.StatusCode} + if err != nil { + // A nil HTTP response means the request never reached the API (e.g. a token + // refresh or other authentication failure in the request wrapper). There is no + // status code to read and no response body to print, so surface the error + // directly. This also avoids a nil-pointer dereference on httpResponse below + // when the fail-on-http-error flag is set. + if httpResponse == nil { + return nil, nil, err + } + + if failOnHTTPError { + return nil, nil, translatableerror.CurlExit22Error{StatusCode: httpResponse.StatusCode} + } } return responseBody, httpResponse, nil diff --git a/actor/v7action/curl_test.go b/actor/v7action/curl_test.go index 4f6120b425c..fce810dfc20 100644 --- a/actor/v7action/curl_test.go +++ b/actor/v7action/curl_test.go @@ -247,5 +247,33 @@ var _ = Describe("Curl Actions", func() { }) }) }) + + When("the request fails before an HTTP response is received", func() { + // e.g. a token refresh / authentication failure in the request wrapper, where + // no request reaches the API and there is no HTTP response. + BeforeEach(func() { + mockErr = errors.New("Bad credentials") + mockResponseBody = nil + mockHTTPResponse = nil + }) + + It("surfaces the error instead of returning empty output", func() { + Expect(executeErr).To(MatchError("Bad credentials")) + Expect(responseBody).To(BeNil()) + Expect(httpResponse).To(BeNil()) + }) + + When("the fail-on-http-errors flag is set", func() { + BeforeEach(func() { + failOnHTTPError = true + }) + + It("surfaces the error without panicking on the nil response", func() { + Expect(executeErr).To(MatchError("Bad credentials")) + Expect(responseBody).To(BeNil()) + Expect(httpResponse).To(BeNil()) + }) + }) + }) }) })