mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-07 13:00:59 +08:00
security: configurable CORS origins, limit sentinel response body
- Add CORS_ALLOWED_ORIGINS config (comma-separated origins, default allow-all) - CORS middleware now validates Origin header against allowlist - Add io.LimitReader to sentinel.go (was unbounded, now 256KB) - Include previous: agent chat auth middleware + frontend token header
This commit is contained in:
@@ -7,8 +7,11 @@ import (
|
||||
)
|
||||
|
||||
// RegisterAgentHandler registers NOFXi agent API routes on the main router.
|
||||
// Chat endpoint requires authentication; market data endpoints are public.
|
||||
func (s *Server) RegisterAgentHandler(h *agent.WebHandler) {
|
||||
s.router.POST("/api/agent/chat", gin.WrapF(h.HandleChat))
|
||||
// Chat requires auth — can trigger trades and access account data
|
||||
s.router.POST("/api/agent/chat", s.authMiddleware(), gin.WrapF(h.HandleChat))
|
||||
// Public endpoints — read-only market data
|
||||
s.router.GET("/api/agent/health", gin.WrapF(h.HandleHealth))
|
||||
s.router.GET("/api/agent/klines", gin.WrapF(h.HandleKlines))
|
||||
s.router.GET("/api/agent/ticker", gin.WrapF(h.HandleTicker))
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"nofx/auth"
|
||||
"nofx/config"
|
||||
"nofx/crypto"
|
||||
"nofx/logger"
|
||||
"nofx/manager"
|
||||
@@ -56,12 +57,44 @@ func NewServer(traderManager *manager.TraderManager, st *store.Store, cryptoServ
|
||||
return s
|
||||
}
|
||||
|
||||
// corsMiddleware CORS middleware
|
||||
// corsMiddleware CORS middleware with configurable allowed origins.
|
||||
// Set CORS_ALLOWED_ORIGINS env var to a comma-separated list of origins
|
||||
// (e.g. "http://localhost:5173,https://nofx.example.com").
|
||||
// If empty or "*", all origins are allowed (development mode).
|
||||
func corsMiddleware() gin.HandlerFunc {
|
||||
cfg := config.Get()
|
||||
raw := strings.TrimSpace(cfg.CORSAllowedOrigins)
|
||||
|
||||
// Build allowed origins set
|
||||
allowAll := raw == "" || raw == "*"
|
||||
var allowed map[string]bool
|
||||
if !allowAll {
|
||||
allowed = make(map[string]bool)
|
||||
for _, o := range strings.Split(raw, ",") {
|
||||
o = strings.TrimSpace(o)
|
||||
if o != "" {
|
||||
allowed[o] = true
|
||||
}
|
||||
}
|
||||
if len(allowed) == 0 {
|
||||
allowAll = true
|
||||
}
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
origin := c.Request.Header.Get("Origin")
|
||||
if allowAll {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
} else if origin != "" && allowed[origin] {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
c.Writer.Header().Set("Vary", "Origin")
|
||||
} else if origin != "" {
|
||||
// Origin not in allow list — reject preflight, allow simple requests
|
||||
// but without CORS headers the browser will block the response.
|
||||
}
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusOK)
|
||||
|
||||
Reference in New Issue
Block a user