mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-12 23:36:55 +08:00
security: login rate limiting, fix health endpoint, harden error handling
- Fix health endpoint returning null time (use time.Now() instead of context value) - Add LoginRateLimiter: 5 attempts per 15min window, 5min block on brute-force - Apply rate limiting to login/register endpoints - Move reset-password to authenticated routes (was unauthenticated — critical vuln) - Limit response body sizes on proxy/external API calls (prevent memory exhaustion) - Sanitize error messages in agent chat endpoint (don't leak internal errors) - Record login success/failure for rate limiting tracking
This commit is contained in:
@@ -66,7 +66,7 @@ func (b *Brain) scanNews(seen map[string]bool) {
|
||||
resp, err := b.http.Get("https://min-api.cryptocompare.com/data/v2/news/?lang=EN&sortOrder=latest")
|
||||
if err != nil { return }
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 1024*1024)) // 1MB limit
|
||||
if err != nil { return }
|
||||
|
||||
var result struct {
|
||||
@@ -138,7 +138,7 @@ func (b *Brain) sendBrief(hour int) {
|
||||
for _, sym := range []string{"BTCUSDT", "ETHUSDT"} {
|
||||
resp, err := b.http.Get(fmt.Sprintf("https://fapi.binance.com/fapi/v1/ticker/24hr?symbol=%s", sym))
|
||||
if err != nil { continue }
|
||||
body, readErr := io.ReadAll(resp.Body)
|
||||
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 64*1024)) // 64KB limit
|
||||
resp.Body.Close()
|
||||
if readErr != nil { continue }
|
||||
var t map[string]string
|
||||
|
||||
@@ -287,7 +287,7 @@ func fetchStockQuote(code string) (*StockQuote, error) {
|
||||
if err != nil { return nil, err }
|
||||
defer resp.Body.Close()
|
||||
|
||||
reader := transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())
|
||||
reader := transform.NewReader(io.LimitReader(resp.Body, 256*1024), simplifiedchinese.GBK.NewDecoder())
|
||||
body, err := io.ReadAll(reader)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
|
||||
@@ -65,7 +65,8 @@ func (w *WebHandler) HandleChat(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
resp, err := w.agent.HandleMessage(ctx, req.UserID, msg)
|
||||
if err != nil {
|
||||
writeJSON(rw, 500, map[string]string{"error": err.Error()})
|
||||
w.logger.Error("agent HandleMessage failed", "error", err, "user_id", req.UserID)
|
||||
writeJSON(rw, 500, map[string]string{"error": "Failed to process message. Please try again."})
|
||||
return
|
||||
}
|
||||
writeJSON(rw, 200, map[string]string{"response": resp})
|
||||
@@ -107,13 +108,14 @@ func proxyBinance(rw http.ResponseWriter, url string) {
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
writeJSON(rw, 502, map[string]string{"error": err.Error()})
|
||||
writeJSON(rw, 502, map[string]string{"error": "upstream request failed"})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
io.Copy(rw, resp.Body)
|
||||
// Limit response body to 2MB to prevent memory exhaustion
|
||||
io.Copy(rw, io.LimitReader(resp.Body, 2*1024*1024))
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
||||
|
||||
Reference in New Issue
Block a user