소스 검색

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