Files
zitadel/internal/api/http/client_test.go
8e82ec1cb9 Merge commit from fork
* Add DenyLists parsing

* Remove unneeded returned error

* Plug global denylist into Command

* app creation: apply denylist to backchannel logout URI

* Inject denylist to backchannel logout worker

* webhook config: validate against blocked URLs

* Add notificationsWebhook denylist target

* command: Add SMTP endpoint validation against blocklist

* command: Add SMS endpoint validation against blocklist

* Validate webhook endpoint against denylist on channel notification

* Remove unused tests

* handle deprecated denylists

* remove unintended denylist entry in deprecated list

* use single http client

* fix tests

* update comments

* fixes

* cleanup

* address comments

* fix merge

---------

Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
2026-06-15 15:36:14 +02:00

145 lines
3.7 KiB
Go

package http
import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/denylist"
)
func TestClientConfig_NewClient_Allowed(t *testing.T) {
t.Parallel()
// Spin up a simple local test server to serve a dummy response
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("hello world"))
}))
defer server.Close()
// An empty denylist permits all safe destinations
cfg := &ClientConfig{
MaxBodySize: 1024,
Timeout: 2 * time.Second,
MaxRedirects: 3,
DenyList: []denylist.AddressChecker{},
}
client := cfg.NewClient()
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil)
assert.NoError(t, err)
resp, err := client.Do(req)
assert.NoError(t, err)
if resp != nil {
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.Equal(t, "hello world", string(body))
}
}
func TestClientConfig_Redirect_HTTPSDowngradeBlocked(t *testing.T) {
t.Parallel()
cfg := &ClientConfig{
MaxBodySize: 1024,
Timeout: 2 * time.Second,
MaxRedirects: 3,
AllowHTTPSDowngrade: false, // Explicitly disallow downgrades
DenyList: []denylist.AddressChecker{},
}
client := cfg.NewClient()
// Mock a historical request chain moving from a secure schema to an unencrypted target
viaReq, _ := http.NewRequest(http.MethodGet, "https://secure-identity.com/oauth", nil)
currentReq, _ := http.NewRequest(http.MethodGet, "http://insecure-identity.com/callback", nil)
err := client.CheckRedirect(currentReq, []*http.Request{viaReq})
assert.ErrorIs(t, err, ErrHTTPsDowngrade)
}
func TestMaxBytesRoundTripper_EnforcesCap(t *testing.T) {
t.Parallel()
// Spin up a local server delivering an exact 10-byte payload response string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("0123456789"))
}))
defer server.Close()
tests := []struct {
name string
maxBytes int64
expectError error
}{
{
name: "body cleanly within threshold limit",
maxBytes: 15,
expectError: nil,
},
{
name: "body matches boundary size exactly",
maxBytes: 10,
expectError: nil,
},
{
name: "body exceeds allowable limit",
maxBytes: 5,
expectError: ErrResponseTooLarge,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &ClientConfig{
MaxBodySize: tt.maxBytes,
Timeout: 2 * time.Second,
MaxRedirects: 3,
DenyList: []denylist.AddressChecker{},
}
client := cfg.NewClient()
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil)
resp, err := client.Do(req)
if tt.expectError != nil {
// Depending on optimization, errors can trip directly on call or during chunk reading
if err != nil {
assert.ErrorIs(t, err, tt.expectError)
return
}
defer resp.Body.Close()
_, readErr := io.ReadAll(resp.Body)
assert.ErrorIs(t, readErr, tt.expectError)
} else {
assert.NoError(t, err)
defer resp.Body.Close()
body, readErr := io.ReadAll(resp.Body)
assert.NoError(t, readErr)
assert.Equal(t, 10, len(body))
}
})
}
}
func TestClientConfig_MergeDeprecatedDenylists(t *testing.T) {
t.Parallel()
cfg := &ClientConfig{DenyList: []denylist.AddressChecker{denylist.NewHostChecker("1.1.1.1")}}
v1 := []denylist.AddressChecker{denylist.NewHostChecker("2.2.2.2")}
v2 := []denylist.AddressChecker{denylist.NewHostChecker("3.3.3.3")}
cfg.MergeDeprecatedDenylists(v1, v2)
assert.Len(t, cfg.DenyList, 3)
}