mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-13 08:02:34 +00:00
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
109 lines
3.6 KiB
Go
109 lines
3.6 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func TestSecurityHeadersAreSet(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
r.Use(SecurityHeaders())
|
|
r.GET("/x", func(c *gin.Context) { c.Status(http.StatusOK) })
|
|
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/x", nil))
|
|
|
|
want := map[string]string{
|
|
"Cache-Control": "no-store",
|
|
"X-Content-Type-Options": "nosniff",
|
|
"X-Frame-Options": "DENY",
|
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
|
"Cross-Origin-Opener-Policy": "same-origin",
|
|
"Cross-Origin-Resource-Policy": "same-site",
|
|
}
|
|
for k, v := range want {
|
|
if got := w.Header().Get(k); got != v {
|
|
t.Errorf("%s = %q, want %q", k, got, v)
|
|
}
|
|
}
|
|
if w.Header().Get("Content-Security-Policy") == "" {
|
|
t.Error("a default Content-Security-Policy should be set")
|
|
}
|
|
}
|
|
|
|
func TestSecurityHeadersPreventCachingErrors(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
for _, status := range []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusInternalServerError} {
|
|
t.Run(http.StatusText(status), func(t *testing.T) {
|
|
r := gin.New()
|
|
r.Use(SecurityHeaders())
|
|
r.GET("/x", func(c *gin.Context) { c.AbortWithStatus(status) })
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/x", nil))
|
|
if got := w.Header().Get("Cache-Control"); got != "no-store" {
|
|
t.Errorf("Cache-Control = %q, want no-store", got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHandlerPublicCachePolicyWins(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
r.Use(SecurityHeaders())
|
|
r.GET("/public/image.png", func(c *gin.Context) {
|
|
c.Header("Cache-Control", "public, max-age=31536000, immutable")
|
|
c.Status(http.StatusOK)
|
|
})
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/public/image.png", nil))
|
|
if got := w.Header().Get("Cache-Control"); got != "public, max-age=31536000, immutable" {
|
|
t.Errorf("handler cache policy should survive, got %q", got)
|
|
}
|
|
}
|
|
|
|
// HSTS over plain HTTP is ignored by browsers and would pin a self-hosted LAN
|
|
// instance to a scheme it does not serve, so it is only sent on a TLS request.
|
|
func TestHSTSOnlyOverTLS(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
r.Use(SecurityHeaders())
|
|
r.GET("/x", func(c *gin.Context) { c.Status(http.StatusOK) })
|
|
|
|
plain := httptest.NewRecorder()
|
|
r.ServeHTTP(plain, httptest.NewRequest(http.MethodGet, "/x", nil))
|
|
if got := plain.Header().Get("Strict-Transport-Security"); got != "" {
|
|
t.Errorf("no HSTS expected over plain HTTP, got %q", got)
|
|
}
|
|
|
|
forwarded := httptest.NewRecorder()
|
|
req := httptest.NewRequest(http.MethodGet, "/x", nil)
|
|
req.Header.Set("X-Forwarded-Proto", "https")
|
|
r.ServeHTTP(forwarded, req)
|
|
if got := forwarded.Header().Get("Strict-Transport-Security"); got == "" {
|
|
t.Error("HSTS expected when the edge reports https")
|
|
}
|
|
}
|
|
|
|
// A handler that sets its own policy keeps it: the OAuth bouncer pages need
|
|
// script-src 'unsafe-inline' for the one script that is the page.
|
|
func TestHandlerCSPWins(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
r.Use(SecurityHeaders())
|
|
r.GET("/page", func(c *gin.Context) {
|
|
c.Header("Content-Security-Policy", "default-src 'none'; script-src 'unsafe-inline'")
|
|
c.Status(http.StatusOK)
|
|
})
|
|
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/page", nil))
|
|
if got := w.Header().Get("Content-Security-Policy"); got != "default-src 'none'; script-src 'unsafe-inline'" {
|
|
t.Errorf("handler policy should survive, got %q", got)
|
|
}
|
|
}
|