Преглед изворни кода

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

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