Files
go_blog/middleware/security_headers_test.go
T
kevin f307781f58 docs: 全部 Go 代码注释汉化
- 48 个 Go 文件所有注释(行注释/块注释/行尾注释,含 _test.go)翻译为中文
- 保留技术标识符:SECURITY_TODO(n)、unsafe-inline、sqlite/mysql、路由参数等
- 代码、字符串字面量、日志消息保持英文原文,零逻辑改动
- go build/vet 通过,go test -count=1 ./... 全绿
2026-08-27 19:03:03 +08:00

103 lines
2.9 KiB
Go

package middleware
import (
"crypto/tls"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func newHeadersTestRouter() *gin.Engine {
gin.SetMode(gin.TestMode)
r := gin.New()
r.Use(SecurityHeaders())
r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "ok") })
return r
}
func TestSecurityHeadersPresent(t *testing.T) {
r := newHeadersTestRouter()
// 明文 HTTP 请求:加固头存在,无 HSTS。
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
for _, h := range []string{
"Content-Security-Policy",
"X-Content-Type-Options",
"X-Frame-Options",
"Referrer-Policy",
"Permissions-Policy",
} {
if v := w.Header().Get(h); v == "" {
t.Errorf("missing header %s", h)
}
}
if w.Header().Get("X-Content-Type-Options") != "nosniff" {
t.Errorf("X-Content-Type-Options = %q, want nosniff", w.Header().Get("X-Content-Type-Options"))
}
if w.Header().Get("X-Frame-Options") != "DENY" {
t.Errorf("X-Frame-Options = %q, want DENY", w.Header().Get("X-Frame-Options"))
}
if w.Header().Get("Content-Security-Policy") == "" {
t.Error("CSP header missing")
}
if w.Header().Get("Strict-Transport-Security") != "" {
t.Errorf("HSTS must be absent over plain HTTP, got %q", w.Header().Get("Strict-Transport-Security"))
}
}
func TestSecurityHeadersHSTSOverHTTPS(t *testing.T) {
r := newHeadersTestRouter()
// TLS 请求:HSTS 存在。
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.TLS = &tls.ConnectionState{}
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
if v := w.Header().Get("Strict-Transport-Security"); v != "max-age=31536000" {
t.Errorf("HSTS over TLS = %q, want max-age=31536000", v)
}
// 位于可信代理之后(X-Forwarded-Proto: https):HSTS 存在。
req = httptest.NewRequest(http.MethodGet, "/", nil)
req.Header.Set("X-Forwarded-Proto", "https")
w = httptest.NewRecorder()
r.ServeHTTP(w, req)
if v := w.Header().Get("Strict-Transport-Security"); v != "max-age=31536000" {
t.Errorf("HSTS behind proxy = %q, want max-age=31536000", v)
}
}
func TestIsHTTPSRequest(t *testing.T) {
// TLS 请求。
if !IsHTTPSRequest(&gin.Context{Request: mustTLSRequest()}) {
t.Error("TLS request must be HTTPS")
}
// 明文请求。
c := &gin.Context{}
c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
if IsHTTPSRequest(c) {
t.Error("plain request must not be HTTPS")
}
// X-Forwarded-Proto: https。
c.Request.Header.Set("X-Forwarded-Proto", "https")
if !IsHTTPSRequest(c) {
t.Error("X-Forwarded-Proto https must be treated as HTTPS")
}
// X-Forwarded-Proto: http 不得触发 HTTPS 行为。
c.Request.Header.Set("X-Forwarded-Proto", "http")
if IsHTTPSRequest(c) {
t.Error("X-Forwarded-Proto http must not be treated as HTTPS")
}
}
func mustTLSRequest() *http.Request {
r := httptest.NewRequest(http.MethodGet, "/", nil)
r.TLS = &tls.ConnectionState{}
return r
}