fix: 过滤非法 CORS Origin,避免 Docker 启动 panic

gin-contrib/cors 不接受 tauri://,启动时过滤仅保留 http/https。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-12 12:09:06 +08:00
1 parent cb1a9be2c4
commit ac31cc059a
2 files changed
+47 -13

No files matched your search

+30 -12
View File
@@ -48,18 +48,22 @@ func Load() *Config {
DBPassword: getEnv("DB_PASSWORD", ""), DBPassword: getEnv("DB_PASSWORD", ""),
DBName: getEnv("DB_NAME", "db.sqlite3"), DBName: getEnv("DB_NAME", "db.sqlite3"),
JWTSecret: getEnv("JWT_SECRET", ""), JWTSecret: getEnv("JWT_SECRET", ""),
CORSOrigins: []string{ CORSOrigins: defaultCORSOrigins(),
"http://localhost:5181", }
"http://127.0.0.1:5181",
"http://localhost:3000", if raw := strings.TrimSpace(os.Getenv("CORS_ORIGINS")); raw != "" {
"http://127.0.0.1:3000", parts := strings.Split(raw, ",")
// OpsXShell Tauri / Vite dev origins origins := make([]string, 0, len(parts))
"http://localhost:1420", for _, p := range parts {
"http://127.0.0.1:1420", p = strings.TrimSpace(p)
"http://tauri.localhost", if p == "" {
"https://tauri.localhost", continue
"tauri://localhost", }
}, origins = append(origins, p)
}
if len(origins) > 0 {
cfg.CORSOrigins = origins
}
} }
// 安全检查:生产环境拒绝空密钥,开发环境使用进程级随机密钥避免固定后门。 // Logan.Li // 安全检查:生产环境拒绝空密钥,开发环境使用进程级随机密钥避免固定后门。 // Logan.Li
@@ -74,6 +78,20 @@ func Load() *Config {
return cfg return cfg
} }
func defaultCORSOrigins() []string {
return []string{
"http://localhost:5181",
"http://127.0.0.1:5181",
"http://localhost:3000",
"http://127.0.0.1:3000",
// OpsXShell Tauri / Vite dev origins (gin-cors only accepts http/https)
"http://localhost:1420",
"http://127.0.0.1:1420",
"http://tauri.localhost",
"https://tauri.localhost",
}
}
// IsOriginAllowed 判断浏览器 Origin 是否在显式 CORS 白名单中。 // Logan.Li // IsOriginAllowed 判断浏览器 Origin 是否在显式 CORS 白名单中。 // Logan.Li
func (c *Config) IsOriginAllowed(origin string) bool { func (c *Config) IsOriginAllowed(origin string) bool {
for _, allowed := range c.CORSOrigins { for _, allowed := range c.CORSOrigins {
+17 -1
View File
@@ -2,6 +2,7 @@ package middleware
import ( import (
"backend/config" "backend/config"
"strings"
"time" "time"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
@@ -10,7 +11,7 @@ import (
// CORSMiddleware CORS 中间件,使用显式白名单避免携带凭据时放行任意来源。 // Logan.Li // CORSMiddleware CORS 中间件,使用显式白名单避免携带凭据时放行任意来源。 // Logan.Li
func CORSMiddleware(cfg *config.Config) gin.HandlerFunc { func CORSMiddleware(cfg *config.Config) gin.HandlerFunc {
origins := cfg.CORSOrigins origins := filterHTTPOrigins(cfg.CORSOrigins)
if len(origins) == 0 { if len(origins) == 0 {
origins = []string{ origins = []string{
"http://localhost:5181", "http://localhost:5181",
@@ -29,3 +30,18 @@ func CORSMiddleware(cfg *config.Config) gin.HandlerFunc {
MaxAge: 12 * time.Hour, MaxAge: 12 * time.Hour,
}) })
} }
// filterHTTPOrigins keeps only origins gin-contrib/cors accepts (http/https or *). // Logan.Li
func filterHTTPOrigins(origins []string) []string {
out := make([]string, 0, len(origins))
for _, origin := range origins {
o := strings.TrimSpace(origin)
if o == "" {
continue
}
if o == "*" || strings.HasPrefix(o, "http://") || strings.HasPrefix(o, "https://") {
out = append(out, o)
}
}
return out
}