package panel
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func newTestPanel() *Panel {
// 启用鉴权:未带 key 的请求一律 401,不进入依赖 Pool/Upstream 的 handler。
return New(Config{Version: "test", APIKey: "test-key"})
}
// 面板安全响应头必须覆盖:页面、静态脚本、鉴权失败响应。
func TestSecurityHeadersOnAllPanelResponses(t *testing.T) {
p := newTestPanel()
paths := []struct{ method, path string }{
{"GET", "/panel/"},
{"GET", "/panel/app.js"},
{"GET", "/panel/api/overview"}, // 401(未提供 key)
{"POST", "/panel/api/config"}, // 401
{"GET", "/panel/api/nonexistent"},
}
for _, c := range paths {
rec := httptest.NewRecorder()
p.ServeHTTP(rec, httptest.NewRequest(c.method, c.path, nil))
h := rec.Header()
if got := h.Get("Content-Security-Policy"); got == "" {
t.Errorf("%s %s: missing CSP", c.method, c.path)
}
if h.Get("X-Content-Type-Options") != "nosniff" {
t.Errorf("%s %s: X-Content-Type-Options=%q", c.method, c.path, h.Get("X-Content-Type-Options"))
}
if h.Get("X-Frame-Options") != "DENY" {
t.Errorf("%s %s: X-Frame-Options=%q", c.method, c.path, h.Get("X-Frame-Options"))
}
if h.Get("Referrer-Policy") != "no-referrer" {
t.Errorf("%s %s: Referrer-Policy=%q", c.method, c.path, h.Get("Referrer-Policy"))
}
}
}
// CSP 必须禁止内联脚本与 iframe 嵌套(严格策略的核心约束)。
func TestCSPDisallowsInlineScriptAndFraming(t *testing.T) {
p := newTestPanel()
rec := httptest.NewRecorder()
p.ServeHTTP(rec, httptest.NewRequest("GET", "/panel/", nil))
csp := rec.Header().Get("Content-Security-Policy")
for _, must := range []string{
"script-src 'self'",
"frame-ancestors 'none'",
"base-uri 'none'",
"default-src 'none'",
} {
if !strings.Contains(csp, must) {
t.Errorf("CSP missing %q; got: %s", must, csp)
}
}
if strings.Contains(csp, "script-src 'self' 'unsafe-inline'") || strings.Contains(csp, "script-src 'unsafe-inline'") {
t.Errorf("CSP must not allow unsafe-inline scripts; got: %s", csp)
}
}
// 页面必须引用外部脚本(内联脚本会被上面的 CSP 拦掉,页面将完全不可用)。
func TestIndexReferencesExternalScript(t *testing.T) {
p := newTestPanel()
rec := httptest.NewRecorder()
p.ServeHTTP(rec, httptest.NewRequest("GET", "/panel/", nil))
body := rec.Body.String()
if !strings.Contains(body, ``) {
t.Error("index.html must load app.js externally (inline script is blocked by CSP)")
}
// 反例保护:出现内联 内容块即为回归
if strings.Contains(body, "