فهرست منبع

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.
MHSanaei 1 روز پیش
والد
کامیت
61b59bb868
2فایلهای تغییر یافته به همراه43 افزوده شده و 1 حذف شده
  1. 1 1
      internal/web/service/integration/warp.go
  2. 42 0
      internal/web/service/integration/warp_response_test.go

+ 1 - 1
internal/web/service/integration/warp.go

@@ -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
 	}

+ 42 - 0
internal/web/service/integration/warp_response_test.go

@@ -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)
+	}
+}