From 982ee668c9b64fcecd111f1c1719923cebdd555f Mon Sep 17 00:00:00 2001 From: shinchan-zhai Date: Mon, 23 Mar 2026 10:55:03 +0800 Subject: [PATCH] =?UTF-8?q?security:=20add=20safe.ReadAllLimited=20?= =?UTF-8?q?=E2=80=94=20bound=20all=20HTTP=20response=20body=20reads=20to?= =?UTF-8?q?=2010MB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created safe/io.go with ReadAllLimited helper (default 10MB limit) - Replaced 62 unbounded io.ReadAll(resp.Body) calls across 32 files - Covers all exchange traders (Hyperliquid, Bybit, Binance, OKX, Aster, KuCoin, Gate, Bitget, Lighter, Indodax), providers (CoinAnk, Alpaca, TwelveData), MCP client/x402, market data, wallet, telegram, kernel - Prevents OOM from malicious/buggy exchange API responses - Previously fixed: brain.go, sentinel.go already had manual LimitReader --- agent/stock.go | 5 +++-- kernel/engine.go | 4 ++-- market/api_client.go | 8 ++++---- market/data.go | 6 +++--- market/historical.go | 4 ++-- mcp/client.go | 8 ++++---- mcp/payment/x402.go | 12 +++++------ nofxi-tasks.md | 1 + provider/alpaca/kline.go | 4 ++-- provider/coinank/coinank_api/kline.go | 4 ++-- provider/coinank/coinank_http.go | 6 +++--- provider/hyperliquid/kline.go | 8 ++++---- provider/twelvedata/kline.go | 6 +++--- safe/io.go | 29 +++++++++++++++++++++++++++ telegram/agent/apicall.go | 3 ++- trader/aster/trader.go | 8 ++++---- trader/aster/trader_account.go | 6 +++--- trader/bitget/trader.go | 4 ++-- trader/bybit/order_sync.go | 3 +-- trader/bybit/trader.go | 4 ++-- trader/bybit/trader_account.go | 4 ++-- trader/bybit/trader_orders.go | 4 ++-- trader/hyperliquid/trader_account.go | 6 +++--- trader/hyperliquid/trader_orders.go | 10 ++++----- trader/hyperliquid/trader_sync.go | 4 ++-- trader/indodax/trader.go | 6 +++--- trader/kucoin/trader.go | 6 +++--- trader/lighter/account.go | 8 ++++---- trader/lighter/orders.go | 6 +++--- trader/lighter/trader.go | 8 ++++---- trader/lighter/trading.go | 6 +++--- trader/okx/trader.go | 4 ++-- wallet/usdc.go | 4 ++-- 33 files changed, 120 insertions(+), 89 deletions(-) create mode 100644 safe/io.go diff --git a/agent/stock.go b/agent/stock.go index d03bb633..8b2d2b04 100644 --- a/agent/stock.go +++ b/agent/stock.go @@ -1,6 +1,7 @@ package agent import ( + "nofx/safe" "fmt" "io" "net/http" @@ -134,7 +135,7 @@ func searchStock(keyword string) ([]SearchResult, error) { defer resp.Body.Close() reader := transform.NewReader(io.LimitReader(resp.Body, 256*1024), simplifiedchinese.GBK.NewDecoder()) - body, err := io.ReadAll(reader) + body, err := safe.ReadAllLimited(reader) if err != nil { return nil, err } @@ -288,7 +289,7 @@ func fetchStockQuote(code string) (*StockQuote, error) { defer resp.Body.Close() reader := transform.NewReader(io.LimitReader(resp.Body, 256*1024), simplifiedchinese.GBK.NewDecoder()) - body, err := io.ReadAll(reader) + body, err := safe.ReadAllLimited(reader) if err != nil { return nil, err } line := string(body) diff --git a/kernel/engine.go b/kernel/engine.go index 1fe9c958..2cda1639 100644 --- a/kernel/engine.go +++ b/kernel/engine.go @@ -1,10 +1,10 @@ package kernel import ( + "nofx/safe" "context" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "nofx/market" @@ -616,7 +616,7 @@ func (e *StrategyEngine) fetchSingleExternalSource(source store.ExternalDataSour } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, err } diff --git a/market/api_client.go b/market/api_client.go index 47ccff4d..cf74bec8 100644 --- a/market/api_client.go +++ b/market/api_client.go @@ -1,9 +1,9 @@ package market import ( + "nofx/safe" "encoding/json" "fmt" - "io" "log" "net/http" "nofx/hook" @@ -43,7 +43,7 @@ func (c *APIClient) GetExchangeInfo() (*ExchangeInfo, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, err } @@ -75,7 +75,7 @@ func (c *APIClient) GetKlines(symbol, interval string, limit int) ([]Kline, erro } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, err } @@ -140,7 +140,7 @@ func (c *APIClient) GetCurrentPrice(symbol string) (float64, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return 0, err } diff --git a/market/data.go b/market/data.go index 41548348..30b1d6b6 100644 --- a/market/data.go +++ b/market/data.go @@ -1,9 +1,9 @@ package market import ( + "nofx/safe" "encoding/json" "fmt" - "io" "math" "nofx/logger" "strconv" @@ -266,7 +266,7 @@ func getOpenInterestData(symbol string) (*OIData, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, err } @@ -311,7 +311,7 @@ func getFundingRate(symbol string) (float64, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return 0, err } diff --git a/market/historical.go b/market/historical.go index 9d401b5d..18d8f51a 100644 --- a/market/historical.go +++ b/market/historical.go @@ -1,9 +1,9 @@ package market import ( + "nofx/safe" "encoding/json" "fmt" - "io" "net/http" "time" ) @@ -51,7 +51,7 @@ func GetKlinesRange(symbol string, timeframe string, start, end time.Time) ([]Kl return nil, err } - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) resp.Body.Close() if err != nil { return nil, err diff --git a/mcp/client.go b/mcp/client.go index 4b96c773..1764ddb8 100644 --- a/mcp/client.go +++ b/mcp/client.go @@ -371,7 +371,7 @@ func (client *Client) Call(systemPrompt, userPrompt string) (string, error) { defer resp.Body.Close() // Step 6: Read response body (fixed logic) - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return "", fmt.Errorf("failed to read response: %w", err) } @@ -510,7 +510,7 @@ func (client *Client) callWithRequestFull(req *Request) (*LLMResponse, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } @@ -548,7 +548,7 @@ func (client *Client) callWithRequest(req *Request) (string, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return "", fmt.Errorf("failed to read response: %w", err) } @@ -721,7 +721,7 @@ func (client *Client) CallWithRequestStream(req *Request, onChunk func(string)) defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) return "", fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(body)) } diff --git a/mcp/payment/x402.go b/mcp/payment/x402.go index 207fffb3..60e9afe1 100644 --- a/mcp/payment/x402.go +++ b/mcp/payment/x402.go @@ -126,7 +126,7 @@ func DoX402Request( paymentHeader = resp.Header.Get("X-Payment-Required") } if paymentHeader == "" { - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) return nil, fmt.Errorf("received 402 but no Payment-Required header found. Body: %s", string(body)) } @@ -161,7 +161,7 @@ func DoX402Request( return nil, fmt.Errorf("failed to send payment retry: %w", err) } - body2, readErr := io.ReadAll(resp2.Body) + body2, readErr := safe.ReadAllLimited(resp2.Body) resp2.Body.Close() if readErr != nil { return nil, fmt.Errorf("failed to read payment retry response: %w", readErr) @@ -221,7 +221,7 @@ func DoX402Request( return nil, fmt.Errorf("%s payment retry failed (status %d): %s", providerTag, lastStatus, string(lastBody)) } - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } @@ -261,7 +261,7 @@ func DoX402RequestStream( if resp.StatusCode == http.StatusOK { return resp, nil } - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) resp.Body.Close() return nil, fmt.Errorf("%s API error (status %d): %s", providerTag, resp.StatusCode, string(body)) } @@ -272,7 +272,7 @@ func DoX402RequestStream( paymentHeader = resp.Header.Get("X-Payment-Required") } if paymentHeader == "" { - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) resp.Body.Close() return nil, fmt.Errorf("received 402 but no Payment-Required header found. Body: %s", string(body)) } @@ -320,7 +320,7 @@ func DoX402RequestStream( } // Non-200: read body for error handling / re-sign - body2, readErr := io.ReadAll(resp2.Body) + body2, readErr := safe.ReadAllLimited(resp2.Body) resp2.Body.Close() if readErr != nil { return nil, fmt.Errorf("failed to read payment retry response: %w", readErr) diff --git a/nofxi-tasks.md b/nofxi-tasks.md index a8ce122d..6c0b2709 100644 --- a/nofxi-tasks.md +++ b/nofxi-tasks.md @@ -52,6 +52,7 @@ ### Code Quality - [DONE] Extract WelcomeScreen, ChatMessages, ChatInput from AgentChatPage (825→480 lines) +- [DONE] Sanitize 3 more error message leaks in API responses (handler_trader.go ×2, handler_ai_cost.go ×1) - [PENDING] `context.Background()` used in ~69 exchange/trader calls — should propagate request context for proper cancellation (partially done: kline handlers fixed, trader/exchange calls remain) ### Performance diff --git a/provider/alpaca/kline.go b/provider/alpaca/kline.go index 71d07f4c..6bd3a89f 100644 --- a/provider/alpaca/kline.go +++ b/provider/alpaca/kline.go @@ -1,10 +1,10 @@ package alpaca import ( + "nofx/safe" "context" "encoding/json" "fmt" - "io" "net/http" "net/url" "nofx/config" @@ -111,7 +111,7 @@ func (c *Client) GetBars(ctx context.Context, symbol string, timeframe string, l defer resp.Body.Close() // Read response - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/provider/coinank/coinank_api/kline.go b/provider/coinank/coinank_api/kline.go index b17e1938..2fca3d1a 100644 --- a/provider/coinank/coinank_api/kline.go +++ b/provider/coinank/coinank_api/kline.go @@ -1,10 +1,10 @@ package coinank_api import ( + "nofx/safe" "context" "encoding/json" "fmt" - "io" "net/http" "net/url" "nofx/provider/coinank" @@ -67,7 +67,7 @@ func get(ctx context.Context, path string, paramsMap map[string]string) (string, return "", err } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return "", err } diff --git a/provider/coinank/coinank_http.go b/provider/coinank/coinank_http.go index d06b0349..11a653e9 100644 --- a/provider/coinank/coinank_http.go +++ b/provider/coinank/coinank_http.go @@ -1,12 +1,12 @@ package coinank import ( + "nofx/safe" "bytes" "context" "encoding/json" "errors" "fmt" - "io" "net/http" "net/url" "time" @@ -59,7 +59,7 @@ func (c *CoinankClient) Get(ctx context.Context, path string, paramsMap map[stri return "", err } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return "", err } @@ -84,7 +84,7 @@ func (c *CoinankClient) Post(ctx context.Context, path string, data any) (string return "", err } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return "", err } diff --git a/provider/hyperliquid/kline.go b/provider/hyperliquid/kline.go index de682845..12b8ae09 100644 --- a/provider/hyperliquid/kline.go +++ b/provider/hyperliquid/kline.go @@ -1,11 +1,11 @@ package hyperliquid import ( + "nofx/safe" "bytes" "context" "encoding/json" "fmt" - "io" "net/http" "strings" "time" @@ -117,7 +117,7 @@ func (c *Client) GetCandles(ctx context.Context, coin string, interval string, l defer resp.Body.Close() // Read response - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } @@ -169,7 +169,7 @@ func (c *Client) GetAllMidsWithDex(ctx context.Context, dex string) (map[string] } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } @@ -206,7 +206,7 @@ func (c *Client) GetMeta(ctx context.Context) (*Meta, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/provider/twelvedata/kline.go b/provider/twelvedata/kline.go index caa9161a..d31d9e31 100644 --- a/provider/twelvedata/kline.go +++ b/provider/twelvedata/kline.go @@ -1,10 +1,10 @@ package twelvedata import ( + "nofx/safe" "context" "encoding/json" "fmt" - "io" "net/http" "net/url" "nofx/config" @@ -125,7 +125,7 @@ func (c *Client) GetTimeSeries(ctx context.Context, symbol string, interval stri defer resp.Body.Close() // Read response - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } @@ -172,7 +172,7 @@ func (c *Client) GetQuote(ctx context.Context, symbol string) (*QuoteResponse, e defer resp.Body.Close() // Read response - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/safe/io.go b/safe/io.go new file mode 100644 index 00000000..d6fec028 --- /dev/null +++ b/safe/io.go @@ -0,0 +1,29 @@ +// Package safe provides safe I/O helpers. +package safe + +import ( + "fmt" + "io" +) + +// MaxResponseBody is the default maximum size for HTTP response bodies (10MB). +const MaxResponseBody = 10 * 1024 * 1024 + +// ReadAllLimited reads all bytes from r up to maxBytes. +// If maxBytes <= 0, it defaults to MaxResponseBody (10MB). +// Returns an error if the response exceeds the limit. +func ReadAllLimited(r io.Reader, maxBytes ...int64) ([]byte, error) { + limit := int64(MaxResponseBody) + if len(maxBytes) > 0 && maxBytes[0] > 0 { + limit = maxBytes[0] + } + lr := io.LimitReader(r, limit+1) + data, err := io.ReadAll(lr) + if err != nil { + return nil, err + } + if int64(len(data)) > limit { + return nil, fmt.Errorf("response body exceeds %d bytes limit", limit) + } + return data, nil +} diff --git a/telegram/agent/apicall.go b/telegram/agent/apicall.go index eca6b9d5..587ec8e8 100644 --- a/telegram/agent/apicall.go +++ b/telegram/agent/apicall.go @@ -1,6 +1,7 @@ package agent import ( + "nofx/safe" "bytes" "encoding/json" "fmt" @@ -65,7 +66,7 @@ func (t *apiCallTool) execute(req *apiRequest) string { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return fmt.Sprintf("error reading response: %v", err) } diff --git a/trader/aster/trader.go b/trader/aster/trader.go index d7272f21..c820c339 100644 --- a/trader/aster/trader.go +++ b/trader/aster/trader.go @@ -1,12 +1,12 @@ package aster import ( + "nofx/safe" "context" "crypto/ecdsa" "encoding/hex" "encoding/json" "fmt" - "io" "math" "math/big" "net/http" @@ -100,7 +100,7 @@ func (t *AsterTrader) getPrecision(symbol string) (SymbolPrecision, error) { } defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) var info struct { Symbols []struct { Symbol string `json:"symbol"` @@ -392,7 +392,7 @@ func (t *AsterTrader) doRequest(method, endpoint string, params map[string]inter } defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) } @@ -418,7 +418,7 @@ func (t *AsterTrader) doRequest(method, endpoint string, params map[string]inter } defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) } diff --git a/trader/aster/trader_account.go b/trader/aster/trader_account.go index bfc41304..52aecbe4 100644 --- a/trader/aster/trader_account.go +++ b/trader/aster/trader_account.go @@ -1,10 +1,10 @@ package aster import ( + "nofx/safe" "encoding/json" "errors" "fmt" - "io" "net/http" "nofx/logger" "nofx/trader/types" @@ -109,7 +109,7 @@ func (t *AsterTrader) GetMarketPrice(symbol string) (float64, error) { } defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) if resp.StatusCode != http.StatusOK { return 0, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) } @@ -263,7 +263,7 @@ func (t *AsterTrader) GetOrderBook(symbol string, depth int) (bids, asks [][]flo } defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) if resp.StatusCode != http.StatusOK { return nil, nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) } diff --git a/trader/bitget/trader.go b/trader/bitget/trader.go index 449d451e..0242d605 100644 --- a/trader/bitget/trader.go +++ b/trader/bitget/trader.go @@ -1,13 +1,13 @@ package bitget import ( + "nofx/safe" "bytes" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "strconv" @@ -193,7 +193,7 @@ func (t *BitgetTrader) doRequest(method, path string, body interface{}) ([]byte, } defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) + respBody, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/bybit/order_sync.go b/trader/bybit/order_sync.go index 147585b0..6d6a0835 100644 --- a/trader/bybit/order_sync.go +++ b/trader/bybit/order_sync.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "nofx/market" @@ -79,7 +78,7 @@ func (t *BybitTrader) getTradesViaHTTP(startTime time.Time, limit int) ([]BybitT } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/bybit/trader.go b/trader/bybit/trader.go index 5f8e9bfb..631fbab9 100644 --- a/trader/bybit/trader.go +++ b/trader/bybit/trader.go @@ -1,9 +1,9 @@ package bybit import ( + "nofx/safe" "encoding/json" "fmt" - "io" "math" "net/http" "nofx/logger" @@ -101,7 +101,7 @@ func (t *BybitTrader) getQtyStep(symbol string) float64 { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return 1 } diff --git a/trader/bybit/trader_account.go b/trader/bybit/trader_account.go index 88616a04..464fb522 100644 --- a/trader/bybit/trader_account.go +++ b/trader/bybit/trader_account.go @@ -1,13 +1,13 @@ package bybit import ( + "nofx/safe" "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" - "io" "net/http" "nofx/trader/types" "strconv" @@ -134,7 +134,7 @@ func (t *BybitTrader) getClosedPnLViaHTTP(startTime time.Time, limit int) ([]typ } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/bybit/trader_orders.go b/trader/bybit/trader_orders.go index fbf5f5d9..fa600c95 100644 --- a/trader/bybit/trader_orders.go +++ b/trader/bybit/trader_orders.go @@ -1,10 +1,10 @@ package bybit import ( + "nofx/safe" "context" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "nofx/trader/types" @@ -696,7 +696,7 @@ func (t *BybitTrader) GetOrderBook(symbol string, depth int) (bids, asks [][]flo } defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + body, _ := safe.ReadAllLimited(resp.Body) if resp.StatusCode != http.StatusOK { return nil, nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) } diff --git a/trader/hyperliquid/trader_account.go b/trader/hyperliquid/trader_account.go index da4e0b63..f733728f 100644 --- a/trader/hyperliquid/trader_account.go +++ b/trader/hyperliquid/trader_account.go @@ -1,10 +1,10 @@ package hyperliquid import ( + "nofx/safe" "bytes" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "nofx/trader/types" @@ -224,7 +224,7 @@ func (t *HyperliquidTrader) getXYZDexBalance() (accountValue float64, unrealized } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return 0, 0, nil, fmt.Errorf("failed to read response: %w", err) } @@ -315,7 +315,7 @@ func (t *HyperliquidTrader) getXyzMarketPrice(coin string) (float64, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return 0, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/hyperliquid/trader_orders.go b/trader/hyperliquid/trader_orders.go index 3543d655..1e35e711 100644 --- a/trader/hyperliquid/trader_orders.go +++ b/trader/hyperliquid/trader_orders.go @@ -1,10 +1,10 @@ package hyperliquid import ( + "nofx/safe" "bytes" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "nofx/trader/types" @@ -452,7 +452,7 @@ func (t *HyperliquidTrader) cancelXyzOrders(coin string) error { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return fmt.Errorf("failed to read response: %w", err) } @@ -545,7 +545,7 @@ func (t *HyperliquidTrader) cancelXyzOrder(oid int64) error { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return fmt.Errorf("failed to read response: %w", err) } @@ -686,7 +686,7 @@ func (t *HyperliquidTrader) placeXyzOrder(coin string, isBuy bool, size float64, } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return fmt.Errorf("failed to read response body: %w", err) } @@ -850,7 +850,7 @@ func (t *HyperliquidTrader) placeXyzTriggerOrder(coin string, isBuy bool, size f } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return fmt.Errorf("failed to read response body: %w", err) } diff --git a/trader/hyperliquid/trader_sync.go b/trader/hyperliquid/trader_sync.go index 5a7a21ec..2fdb6dc6 100644 --- a/trader/hyperliquid/trader_sync.go +++ b/trader/hyperliquid/trader_sync.go @@ -1,10 +1,10 @@ package hyperliquid import ( + "nofx/safe" "bytes" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "strings" @@ -74,7 +74,7 @@ func (t *HyperliquidTrader) fetchXyzMeta() error { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/indodax/trader.go b/trader/indodax/trader.go index b1762ba9..a6d1936e 100644 --- a/trader/indodax/trader.go +++ b/trader/indodax/trader.go @@ -1,12 +1,12 @@ package indodax import ( + "nofx/safe" "crypto/hmac" "crypto/sha512" "encoding/hex" "encoding/json" "fmt" - "io" "net/http" "net/url" "nofx/logger" @@ -131,7 +131,7 @@ func (t *IndodaxTrader) doPublicRequest(path string) ([]byte, error) { } defer resp.Body.Close() - data, err := io.ReadAll(resp.Body) + data, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } @@ -168,7 +168,7 @@ func (t *IndodaxTrader) doPrivateRequest(params url.Values) ([]byte, error) { } defer resp.Body.Close() - data, err := io.ReadAll(resp.Body) + data, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/kucoin/trader.go b/trader/kucoin/trader.go index 746ff634..f949a4ac 100644 --- a/trader/kucoin/trader.go +++ b/trader/kucoin/trader.go @@ -1,13 +1,13 @@ package kucoin import ( + "nofx/safe" "bytes" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" - "io" "math" "net/http" "nofx/logger" @@ -127,7 +127,7 @@ func (t *KuCoinTrader) syncServerTime() error { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return fmt.Errorf("failed to read response: %w", err) } @@ -232,7 +232,7 @@ func (t *KuCoinTrader) doRequest(method, path string, body interface{}) ([]byte, } defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) + respBody, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/lighter/account.go b/trader/lighter/account.go index 39c14130..a85345cc 100644 --- a/trader/lighter/account.go +++ b/trader/lighter/account.go @@ -1,9 +1,9 @@ package lighter import ( + "nofx/safe" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "strconv" @@ -26,7 +26,7 @@ func (t *LighterTraderV2) getFullAccountInfo() (*AccountInfo, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, err } @@ -305,7 +305,7 @@ func (t *LighterTraderV2) GetMarketPrice(symbol string) (float64, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return 0, err } @@ -379,7 +379,7 @@ func (t *LighterTraderV2) GetOrderBook(symbol string, depth int) (bids, asks [][ } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, nil, err } diff --git a/trader/lighter/orders.go b/trader/lighter/orders.go index e6678f1d..16edb832 100644 --- a/trader/lighter/orders.go +++ b/trader/lighter/orders.go @@ -1,9 +1,9 @@ package lighter import ( + "nofx/safe" "encoding/json" "fmt" - "io" "net/http" "net/url" "nofx/logger" @@ -120,7 +120,7 @@ func (t *LighterTraderV2) GetOrderStatus(symbol string, orderID string) (map[str } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } @@ -236,7 +236,7 @@ func (t *LighterTraderV2) GetActiveOrders(symbol string) ([]OrderResponse, error } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/lighter/trader.go b/trader/lighter/trader.go index a7cfe921..86df7f0a 100644 --- a/trader/lighter/trader.go +++ b/trader/lighter/trader.go @@ -1,10 +1,10 @@ package lighter import ( + "nofx/safe" "context" "encoding/json" "fmt" - "io" "math" "net/http" "net/url" @@ -211,7 +211,7 @@ func (t *LighterTraderV2) getAccountByL1Address() (*AccountInfo, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, err } @@ -286,7 +286,7 @@ func (t *LighterTraderV2) getApiKeyFromServer() (string, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return "", err } @@ -484,7 +484,7 @@ func (t *LighterTraderV2) GetTrades(startTime time.Time, limit int) ([]tradertyp } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/lighter/trading.go b/trader/lighter/trading.go index ae617757..d6498908 100644 --- a/trader/lighter/trading.go +++ b/trader/lighter/trading.go @@ -1,10 +1,10 @@ package lighter import ( + "nofx/safe" "bytes" "encoding/json" "fmt" - "io" "mime/multipart" "net/http" "nofx/logger" @@ -389,7 +389,7 @@ func (t *LighterTraderV2) submitOrder(txType int, txInfo string) (map[string]int } defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) + respBody, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, err } @@ -546,7 +546,7 @@ func (t *LighterTraderV2) fetchMarketList() ([]MarketInfo, error) { } defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + body, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/trader/okx/trader.go b/trader/okx/trader.go index b46f23c4..71f913b5 100644 --- a/trader/okx/trader.go +++ b/trader/okx/trader.go @@ -1,6 +1,7 @@ package okx import ( + "nofx/safe" "bytes" "crypto/hmac" "crypto/rand" @@ -9,7 +10,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io" "net/http" "nofx/logger" "strings" @@ -228,7 +228,7 @@ func (t *OKXTrader) doRequest(method, path string, body interface{}) ([]byte, er } defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) + respBody, err := safe.ReadAllLimited(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } diff --git a/wallet/usdc.go b/wallet/usdc.go index 41952724..4da57804 100644 --- a/wallet/usdc.go +++ b/wallet/usdc.go @@ -2,10 +2,10 @@ package wallet import ( + "nofx/safe" "bytes" "encoding/json" "fmt" - "io" "math/big" "net/http" "strings" @@ -61,7 +61,7 @@ func QueryUSDCBalanceStr(address string) string { } defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) + respBody, err := safe.ReadAllLimited(resp.Body) if err != nil { return "0.00" }