package api //nolint:revive import ( "embed" "net/http" "path" "strings" "github.com/gin-gonic/gin" ) //go:embed admin/* var adminAssets embed.FS var adminContentTypes = map[string]string{ ".css": "text/css; charset=utf-8", ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", } func setAdminSecurityHeaders(ctx *gin.Context) { ctx.Header("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self'; "+ "connect-src 'self'; frame-src http: https:; img-src 'self' data:; "+ "object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'self'") ctx.Header("Referrer-Policy", "no-referrer") ctx.Header("X-Content-Type-Options", "nosniff") ctx.Header("X-Frame-Options", "SAMEORIGIN") } func (a *API) onAdminRedirect(ctx *gin.Context) { ctx.Redirect(http.StatusMovedPermanently, "/admin/") } func (a *API) onAdminAsset(ctx *gin.Context) { assetPath := strings.TrimPrefix(ctx.Param("path"), "/") if assetPath == "" { assetPath = "index.html" } assetPath = path.Clean(assetPath) if assetPath == "." || strings.HasPrefix(assetPath, "../") { ctx.Status(http.StatusNotFound) return } byts, err := adminAssets.ReadFile("admin/" + assetPath) if err != nil { ctx.Status(http.StatusNotFound) return } contentType, ok := adminContentTypes[path.Ext(assetPath)] if !ok { ctx.Status(http.StatusNotFound) return } setAdminSecurityHeaders(ctx) if assetPath == "index.html" { ctx.Header("Cache-Control", "no-store") } else { ctx.Header("Cache-Control", "no-cache") } ctx.Data(http.StatusOK, contentType, byts) }