Add files via upload

This commit is contained in:
公明
2026-07-14 11:52:27 +08:00
committed by GitHub
parent 1be10cdf2c
commit 7d1e16b97b
3 changed files with 129 additions and 7 deletions
+54 -6
View File
@@ -84,7 +84,7 @@ func New(cfg *config.Config, log *logger.Logger, configPath string) (*App, error
router := gin.Default()
// CORS中间件
router.Use(corsMiddleware())
router.Use(corsMiddleware(cfg.Server.CORSAllowedOrigins))
// 初始化数据库
dbPath := cfg.Database.Path
@@ -2069,22 +2069,36 @@ func initializeKnowledge(
return knowledgeHandler, nil
}
// corsMiddleware CORS中间件
func corsMiddleware() gin.HandlerFunc {
// corsMiddleware allows same-origin requests, valid Chromium extension
// origins, and exact origins explicitly configured by the operator. CORS is
// not an authentication boundary; API access still requires a valid session.
func corsMiddleware(configuredOrigins []string) gin.HandlerFunc {
allowedOrigins := make(map[string]struct{}, len(configuredOrigins))
for _, origin := range configuredOrigins {
if normalized, ok := normalizeCORSOrigin(origin); ok {
allowedOrigins[normalized] = struct{}{}
}
}
return func(c *gin.Context) {
origin := strings.TrimSpace(c.GetHeader("Origin"))
if origin != "" {
parsed, err := url.Parse(origin)
if err != nil || parsed.Host == "" || !strings.EqualFold(parsed.Host, c.Request.Host) {
c.Writer.Header().Add("Vary", "Origin")
normalized, valid := normalizeCORSOrigin(origin)
_, explicitlyAllowed := allowedOrigins[normalized]
parsed, _ := url.Parse(origin)
sameHost := valid && strings.EqualFold(parsed.Host, c.Request.Host)
browserExtension := valid && isChromiumExtensionOrigin(parsed)
if !sameHost && !browserExtension && !explicitlyAllowed {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "cross-origin request denied"})
return
}
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Add("Vary", "Origin")
}
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
c.Writer.Header().Set("Access-Control-Max-Age", "600")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
@@ -2094,3 +2108,37 @@ func corsMiddleware() gin.HandlerFunc {
c.Next()
}
}
// isChromiumExtensionOrigin accepts only Chrome's canonical 32-character
// extension IDs (letters a-p). It does not allow arbitrary custom schemes or
// web origins, and the extension must separately obtain host permission.
func isChromiumExtensionOrigin(origin *url.URL) bool {
if origin == nil || !strings.EqualFold(origin.Scheme, "chrome-extension") || origin.Port() != "" {
return false
}
id := strings.ToLower(origin.Hostname())
if len(id) != 32 {
return false
}
for _, ch := range id {
if ch < 'a' || ch > 'p' {
return false
}
}
return true
}
// normalizeCORSOrigin validates and canonicalizes a serialized origin. CORS
// origins never contain credentials, paths, query strings, or fragments.
func normalizeCORSOrigin(raw string) (string, bool) {
raw = strings.TrimSpace(raw)
if raw == "" || raw == "*" || strings.EqualFold(raw, "null") {
return "", false
}
parsed, err := url.Parse(raw)
if err != nil || parsed.Scheme == "" || parsed.Host == "" || parsed.User != nil ||
(parsed.Path != "" && parsed.Path != "/") || parsed.RawQuery != "" || parsed.Fragment != "" {
return "", false
}
return strings.ToLower(parsed.Scheme) + "://" + strings.ToLower(parsed.Host), true
}