fix(integration): cap WARP API response body size

doWarpRequest read the response with an unbounded io.ReadAll, unlike the
sibling NordVPN client which already caps every read at maxResponseSize.
A hostile panel egress proxy or a MITM on the Cloudflare WARP endpoint
could stream an arbitrarily large body and force the panel into an
unbounded allocation. Wrap the body in an io.LimitReader(maxResponseSize)
to match the NordVPN client.
This commit is contained in:
MHSanaei
2026-07-15 04:36:30 +02:00
parent 66b239da7d
commit 61b59bb868
2 changed files with 43 additions and 1 deletions
+1 -1
View File
@@ -238,7 +238,7 @@ func (s *WarpService) doWarpRequest(req *http.Request) ([]byte, error) {
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
if err != nil {
return nil, err
}
@@ -0,0 +1,42 @@
package integration
import (
"bytes"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"github.com/mhsanaei/3x-ui/v3/internal/database"
)
// A hostile egress proxy (or a MITM on the WARP endpoint) could stream an
// arbitrarily large body; doWarpRequest must cap the read at maxResponseSize so
// the panel cannot be forced into an unbounded allocation.
func TestDoWarpRequestCapsResponseBody(t *testing.T) {
if err := database.InitDB(filepath.Join(t.TempDir(), "x-ui.db")); err != nil {
t.Fatalf("InitDB: %v", err)
}
t.Cleanup(func() { _ = database.CloseDB() })
oversize := maxResponseSize + 4096
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bytes.Repeat([]byte("a"), oversize))
}))
defer srv.Close()
s := &WarpService{}
req, err := http.NewRequest(http.MethodGet, srv.URL, nil)
if err != nil {
t.Fatalf("NewRequest: %v", err)
}
body, err := s.doWarpRequest(req)
if err != nil {
t.Fatalf("doWarpRequest: %v", err)
}
if len(body) != maxResponseSize {
t.Fatalf("response body not capped: got %d bytes, want %d", len(body), maxResponseSize)
}
}