Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions internal/http/refresh_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ type AuthTransport struct {
}

func (t *AuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
token := t.TokenSource.Token()
if token != "" {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
// Skip re-injecting on redirected hops so stdlib's cross-host strip stays in effect.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SAY-5 thanks for the PR! As far as I can tell, this only fixes the normal redirect hop, which is fine (and a nice fix). I'm wondering if you wanted to additionally include a fix for 401 responses, which will still use the RefreshTransport

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. The 401-retry path is a separate failure mode (refresh + cloned request, where req.Response is nil) so I would rather address it in a follow-up PR with its own test, to keep this one tightly scoped to the redirect leak. I will open that next.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is failing due to an expired token, I think, so I'll look in to fixing that up

if req.Response == nil {
token := t.TokenSource.Token()
if token != "" {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
}
}
if t.UserAgent != "" {
req.Header.Set("User-Agent", t.UserAgent)
Expand Down
38 changes: 38 additions & 0 deletions internal/http/refresh_transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,41 @@ func TestIsTerminalRefreshError(t *testing.T) {
}
}
}

func TestAuthTransport_DoesNotInjectOnRedirectedHop(t *testing.T) {
t.Parallel()

var presignedAuth atomic.Value
presignedAuth.Store("")

presigned := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
presignedAuth.Store(r.Header.Get("Authorization"))
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("payload"))
}))
defer presigned.Close()

api := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, presigned.URL+"/blob?X-Amz-Signature=stub", http.StatusFound)
}))
defer api.Close()

ts := NewTokenSource("bk-token")
transport := &AuthTransport{Base: http.DefaultTransport, TokenSource: ts}

client := &http.Client{Transport: transport}
req, err := http.NewRequest(http.MethodGet, api.URL+"/artifact", nil)
if err != nil {
t.Fatalf("NewRequest: %v", err)
}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("Do: %v", err)
}
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()

if got := presignedAuth.Load().(string); got != "" {
t.Fatalf("Authorization leaked to presigned host: %q", got)
}
}