Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 20 additions & 1 deletion pkg/cmd/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,26 @@ func loginRun(cmd *cobra.Command, f factory.Factory, isPromptEnabled bool, ask q
httpClient.Transport = &http.Transport{}
}

httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
// Handle both direct http.Transport and SpinnerRoundTripper wrapping http.Transport
switch transport := httpClient.Transport.(type) {
case *http.Transport:
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
case *apiclient.SpinnerRoundTripper:
// If the SpinnerRoundTripper's Next is an http.Transport, configure it
if httpTransport, ok := transport.Next.(*http.Transport); ok {
httpTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
} else {
// If Next is not an http.Transport, replace it with one that has SSL verification disabled
transport.Next = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
default:
// Fallback: replace the transport entirely with one that ignores SSL errors
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
}

if inputs.apiKey != "" {
Expand Down
114 changes: 114 additions & 0 deletions pkg/cmd/login/login_ssl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package login_test

import (
"crypto/tls"
"net/http"
"testing"

"github.com/OctopusDeploy/cli/pkg/apiclient"
"github.com/stretchr/testify/assert"
)

// TestSSLIgnoreHandling tests that our SSL ignore logic works with both
// direct http.Transport and SpinnerRoundTripper scenarios
func TestSSLIgnoreHandling(t *testing.T) {
tests := []struct {
name string
transport http.RoundTripper
expectPanic bool
}{
{
name: "Direct http.Transport should work",
transport: &http.Transport{},
expectPanic: false,
},
{
name: "SpinnerRoundTripper with http.Transport should work",
transport: &apiclient.SpinnerRoundTripper{Next: &http.Transport{}},
expectPanic: false,
},
{
name: "SpinnerRoundTripper with default transport should work",
transport: apiclient.NewSpinnerRoundTripper(),
expectPanic: false,
},
{
name: "nil transport should work",
transport: nil,
expectPanic: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client := &http.Client{Transport: tt.transport}

// This simulates the SSL ignore logic from loginRun function
defer func() {
if r := recover(); r != nil {
if !tt.expectPanic {
t.Errorf("Unexpected panic: %v", r)
}
}
}()

// Apply the SSL ignore logic (extracted from loginRun)
applySSLIgnoreLogic(client)

// Verify the SSL configuration was applied correctly
verifySSLConfig(t, client)
})
}
}

// applySSLIgnoreLogic contains the same logic as in loginRun for handling SSL
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

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

The comment should clarify that this function mirrors the SSL ignore logic from the main implementation to ensure test accuracy.

Suggested change
// applySSLIgnoreLogic contains the same logic as in loginRun for handling SSL
// applySSLIgnoreLogic mirrors the SSL ignore logic from the main implementation (loginRun)
// to ensure test accuracy. If the SSL ignore logic in loginRun changes, this function
// should be updated accordingly to keep the test valid.

Copilot uses AI. Check for mistakes.
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.

Comment Updated

func applySSLIgnoreLogic(httpClient *http.Client) {
if httpClient.Transport == nil {
httpClient.Transport = &http.Transport{}
}

// Handle both direct http.Transport and SpinnerRoundTripper wrapping http.Transport
switch transport := httpClient.Transport.(type) {
case *http.Transport:
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
case *apiclient.SpinnerRoundTripper:
// If the SpinnerRoundTripper's Next is an http.Transport, configure it
if httpTransport, ok := transport.Next.(*http.Transport); ok {
httpTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
} else {
// If Next is not an http.Transport, replace it with one that has SSL verification disabled
transport.Next = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

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

This logic is duplicated between the test file and the main implementation. Consider extracting this SSL ignore logic into a shared utility function to reduce code duplication and ensure consistency.

Copilot uses AI. Check for mistakes.
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.

Pushed recommended to remove duplicate logic, and extracted SSL ignore logic to a shared functionality.

default:
// Fallback: replace the transport entirely with one that ignores SSL errors
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
}

// verifySSLConfig checks that the SSL configuration was applied correctly
func verifySSLConfig(t *testing.T, httpClient *http.Client) {
assert.NotNil(t, httpClient.Transport, "Transport should not be nil")

switch transport := httpClient.Transport.(type) {
case *http.Transport:
assert.NotNil(t, transport.TLSClientConfig, "TLS config should be set")
assert.True(t, transport.TLSClientConfig.InsecureSkipVerify, "InsecureSkipVerify should be true")

case *apiclient.SpinnerRoundTripper:
assert.NotNil(t, transport.Next, "SpinnerRoundTripper.Next should not be nil")

if httpTransport, ok := transport.Next.(*http.Transport); ok {
assert.NotNil(t, httpTransport.TLSClientConfig, "Underlying TLS config should be set")
assert.True(t, httpTransport.TLSClientConfig.InsecureSkipVerify, "Underlying InsecureSkipVerify should be true")
} else {
t.Errorf("SpinnerRoundTripper.Next should be *http.Transport, got %T", transport.Next)
}

default:
t.Errorf("Unexpected transport type: %T", transport)
}
}