Files
Agent-System/internal/server/cors.go
T
weichenandCursor 5cd22c7327 fix(authz): harden permissions, chat gate, and project create idempotency
Add project:manage_members, CSP frame-src, task-run member view, CreateUser
tx error handling, ERR_NO_PROJECT chat gate, and creator-scoped project
name/client_key uniqueness to stop duplicate creates and auth gaps.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-20 07:04:57 +00:00

42 lines
1.4 KiB
Go

package server
import (
"net/http"
"strings"
)
// securityCSP keeps scripts and fonts on this origin. style-src allows inline
// styles because KaTeX and Mermaid inject them into the page. connect-src
// 'self' covers same-origin JSON and SSE. frame-src allows same-origin and
// blob: iframes for PDF preview. frame-ancestors none matches X-Frame-Options DENY.
const securityCSP = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data: blob:; connect-src 'self'; frame-src 'self' blob:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'"
func applySecurityHeaders(w http.ResponseWriter) {
h := w.Header()
h.Set("X-Content-Type-Options", "nosniff")
h.Set("X-Frame-Options", "DENY")
h.Set("Referrer-Policy", "strict-origin-when-cross-origin")
h.Set("Content-Security-Policy", securityCSP)
}
func applyCORSHeaders(w http.ResponseWriter, r *http.Request, allowedOrigins []string) {
origin := strings.TrimSpace(r.Header.Get("Origin"))
if origin == "" || len(allowedOrigins) == 0 {
return
}
for _, allowed := range allowedOrigins {
if strings.TrimSpace(allowed) == origin {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Vary", "Origin")
return
}
}
}
func (s *Server) writeCORS(w http.ResponseWriter, r *http.Request) {
if s.cfg == nil {
return
}
applyCORSHeaders(w, r, s.cfg.CORSAllowedOrigins)
}