Skip to content

Commit cc1db5c

Browse files
oten91claude
andcommitted
test: Add redirect-blocking tests for HTTP client
Verifies that the HTTP client does not follow redirects from suppliers. Tests both single redirect (301 to malicious domain) and redirect chain scenarios, confirming the server is only hit once. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d6da342 commit cc1db5c

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

network/http/http_client_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package http
22

33
import (
44
"net/http"
5+
"net/http/httptest"
56
"testing"
67

78
"github.com/stretchr/testify/require"
@@ -114,3 +115,47 @@ func TestEnsureHTTPSuccess(t *testing.T) {
114115
})
115116
}
116117
}
118+
119+
func TestHTTPClientDoesNotFollowRedirects(t *testing.T) {
120+
// maliciousServer simulates a malicious supplier that returns a redirect.
121+
maliciousServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
122+
http.Redirect(w, r, "https://evil.example.com", http.StatusMovedPermanently)
123+
}))
124+
defer maliciousServer.Close()
125+
126+
client := NewDefaultHTTPClientWithDebugMetrics()
127+
128+
req, err := http.NewRequest(http.MethodPost, maliciousServer.URL, nil)
129+
require.NoError(t, err)
130+
131+
resp, err := client.httpClient.Do(req)
132+
require.NoError(t, err)
133+
defer resp.Body.Close()
134+
135+
// The client should return the redirect response as-is, not follow it.
136+
require.Equal(t, http.StatusMovedPermanently, resp.StatusCode,
137+
"client should NOT follow redirects — should return the 301 directly")
138+
require.Equal(t, "https://evil.example.com", resp.Header.Get("Location"),
139+
"redirect Location header should be preserved in the response")
140+
}
141+
142+
func TestHTTPClientDoesNotFollowMultipleRedirects(t *testing.T) {
143+
redirectCount := 0
144+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
145+
redirectCount++
146+
http.Redirect(w, r, "/next", http.StatusFound)
147+
}))
148+
defer server.Close()
149+
150+
client := NewDefaultHTTPClientWithDebugMetrics()
151+
152+
req, err := http.NewRequest(http.MethodPost, server.URL, nil)
153+
require.NoError(t, err)
154+
155+
resp, err := client.httpClient.Do(req)
156+
require.NoError(t, err)
157+
defer resp.Body.Close()
158+
159+
require.Equal(t, 1, redirectCount, "server should only be hit once — no redirect following")
160+
require.Equal(t, http.StatusFound, resp.StatusCode)
161+
}

0 commit comments

Comments
 (0)