diff --git a/admin-server/auth.go b/admin-server/auth.go index 978d243..49ed2fd 100644 --- a/admin-server/auth.go +++ b/admin-server/auth.go @@ -139,9 +139,32 @@ func safePortalRedirect(raw string) string { var portalLoginTemplate = template.Must(template.New("login").Parse(` -统一门户登录

统一门户

登录后访问受保护的内部服务

{{if .Error}}
{{.Error}}
{{end}}
京ICP备00000000号-1
`)) +统一门户登录 + +
+ +

统一门户

登录后访问受保护的内部服务

+{{if .Error}}
{{.Error}}
{{end}} +
+
京ICP备00000000号-1
`)) func renderPortalLogin(w http.ResponseWriter, redirect, errText string) { w.Header().Set("Content-Type", "text/html; charset=utf-8") diff --git a/admin-server/auth_test.go b/admin-server/auth_test.go new file mode 100644 index 0000000..82cc518 --- /dev/null +++ b/admin-server/auth_test.go @@ -0,0 +1,54 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" +) + +// 登录页必须跟随系统深浅色,并提供手动切换(原 portal-auth 的行为, +// 重写模板时曾漏掉)。 +func TestLoginPageThemeSupport(t *testing.T) { + rec := httptest.NewRecorder() + renderPortalLogin(rec, "https://dashy.example.com/", "") + body := rec.Body.String() + for _, want := range []string{ + "prefers-color-scheme:dark", "[data-theme=\"dark\"]", "localStorage.setItem('theme'", + "toggle", "aria-label", + } { + if want == "toggle" { + if !strings.Contains(body, "切换深浅色") { + t.Fatalf("缺少手动切换按钮") + } + continue + } + if !strings.Contains(body, want) { + t.Fatalf("登录页缺少 %q", want) + } + } + if !strings.Contains(rec.Header().Get("Content-Type"), "text/html") { + t.Fatalf("Content-Type 不对") + } + if rec.Header().Get("Cache-Control") != "no-store" { + t.Fatalf("登录页不应被缓存") + } +} + +// redirect 必须被 HTML 转义后写入表单 action,避免注入。 +func TestLoginPageEscapesRedirect(t *testing.T) { + dir := t.TempDir() + oldConf, oldURL := confFile, historyBaseURL + confFile, historyBaseURL = dir+"/conf.yml", "http://127.0.0.1:1" + defer func() { confFile, historyBaseURL = oldConf, oldURL }() + _ = os.WriteFile(confFile, []byte("sections: []\n"), 0o644) + + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/login?redirect=https://evil.example/\">", nil) + handlePortalLogin(rec, req) + body := rec.Body.String() + if strings.Contains(body, "") { + t.Fatalf("redirect 未转义,存在注入:%s", body) + } +}