Closes #282. CLAUDE.md documented the SecurityHeaders() middleware as setting 6 headers (X-Content-Type-Options, X-Frame-Options, Referrer- Policy, Content-Security-Policy, Permissions-Policy, HSTS) but the implementation only set 4 — Referrer-Policy and Permissions-Policy were silently missing. Adds: - Referrer-Policy: strict-origin-when-cross-origin — prevents browsers from leaking full paths/queries in Referer on cross- origin navigation. Particularly relevant for canvas embeds of Langfuse trace URLs that may contain trace IDs. - Permissions-Policy: camera=(), microphone=(), geolocation=() — denies sensor access by default. Iframes the canvas embeds (Langfuse trace viewer etc.) can no longer request these without an explicit delegation. Regression tests added to securityheaders_test.go — both headers are now in the same table-driven assertion loop as the other 4, so a future edit that drops them again fails CI loudly. LOW severity — this is defense-in-depth, not a direct exploit path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
31 lines
1.6 KiB
Go
31 lines
1.6 KiB
Go
package middleware
|
|
|
|
import "github.com/gin-gonic/gin"
|
|
|
|
// SecurityHeaders returns a Gin middleware that sets standard HTTP security
|
|
// headers on every response to mitigate common web-application attacks:
|
|
//
|
|
// - X-Content-Type-Options: nosniff — prevents MIME-type sniffing
|
|
// - X-Frame-Options: DENY — blocks iframe embedding (clickjacking)
|
|
// - Content-Security-Policy: default-src 'self' — restricts resource loading to same origin
|
|
// - Strict-Transport-Security: max-age=31536000; includeSubDomains — enforces HTTPS for 1 year
|
|
// - Referrer-Policy: strict-origin-when-cross-origin — avoids leaking full paths/queries in Referer
|
|
// - Permissions-Policy: camera=(), microphone=(), geolocation=() — denies sensor access for embedded content
|
|
func SecurityHeaders() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.Header("X-Content-Type-Options", "nosniff")
|
|
c.Header("X-Frame-Options", "DENY")
|
|
c.Header("Content-Security-Policy", "default-src 'self'")
|
|
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
|
// #282: these two were documented in CLAUDE.md but missing from
|
|
// the middleware. Referrer-Policy prevents browsers from leaking
|
|
// the full Referer URL to cross-origin resources (which can
|
|
// expose internal paths/queries). Permissions-Policy denies
|
|
// sensor access by default — especially relevant because the
|
|
// canvas embeds iframes for Langfuse traces.
|
|
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
|
|
c.Header("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
|
|
c.Next()
|
|
}
|
|
}
|