Files
ARTEX/server/auth_test.go
BaaRai e233c02314 Translate all Chinese text to English
Comments, doc comments, UI labels, log and error messages, LLM prompt
strings, markdown docs, test data and assertions. Identifiers, enum
keys, routes, placeholders and code structure are unchanged.

Notes:
- docs/漏洞流量证据.md renamed to docs/vulnerability-traffic-evidence.md
- tests that exercised CJK specific handling (GBK zip names, Chinese
  full text search) now use ASCII equivalents; go build passes and the
  test suite matches the pre translation baseline (remaining failures
  need a PostgreSQL DSN and fail identically on the original)
- frontend mention syntax now uses English kinds (finding, asset, ...)
  matching the backend, chat mentions tests pass
2026-10-08 23:14:34 +02:00

85 lines
2.8 KiB
Go

package server
import (
"encoding/json"
"net/http/httptest"
"strings"
"testing"
)
// GetSetting distinguishes "key absent" from "read failed" only through its error:
// both return an empty value. If a password handler mistakes the error for "no
// password set yet", a database blip opens the init door — authInit would let an
// unauthenticated request overwrite the admin password, and authStatus ships the frontend to /setup to do the same.
//
// Both tests close the handlers' connection pool to force a read failure and assert both fail closed.
func TestAuthStatusFailsClosedWhenDataSourceUnavailable(t *testing.T) {
m, err := NewManager(t.TempDir(), "")
if err != nil {
t.Skipf("postgres unavailable (%v)", err)
}
defer m.Close()
// Close the pool so subsequent GetSetting returns an error instead of sql.ErrNoRows.
if err := m.pg.Close(); err != nil {
t.Fatal(err)
}
s := &Server{m: m}
w := httptest.NewRecorder()
s.authStatus(w, httptest.NewRequest("GET", "/api/auth/status", nil))
if w.Code != 503 {
t.Fatalf("status=%d want 503 (treating a failed read as uninitialized sends the user to /setup to overwrite the password); body=%s", w.Code, w.Body.String())
}
var payload struct {
Initialized *bool `json:"initialized"`
}
if err := json.Unmarshal(w.Body.Bytes(), &payload); err == nil && payload.Initialized != nil {
t.Fatalf("a failed read must not answer initialized, got %v", *payload.Initialized)
}
}
func TestAuthInitFailsClosedWhenDataSourceUnavailable(t *testing.T) {
m, err := NewManager(t.TempDir(), "")
if err != nil {
t.Skipf("postgres unavailable (%v)", err)
}
defer m.Close()
if err := m.pg.Close(); err != nil {
t.Fatal(err)
}
s := &Server{m: m}
w := httptest.NewRecorder()
body := strings.NewReader(`{"password":"correct horse battery"}`)
s.authInit(w, httptest.NewRequest("POST", "/api/auth/init", body))
if w.Code != 503 {
t.Fatalf("status=%d want 503 (allowing through on a failed read lets an unauthenticated request overwrite the existing password); body=%s", w.Code, w.Body.String())
}
if strings.Contains(w.Body.String(), "token") {
t.Fatalf("a failed read must not issue a token: %s", w.Body.String())
}
}
func TestValidatePassword(t *testing.T) {
for _, tc := range []struct {
name, pw string
wantErr bool
}{
{"empty", "", true},
{"seven chars", "1234567", true},
{"eight chars", "12345678", false},
{"eight multibyte chars counted as runes not bytes", strings.Repeat("€", 8), false},
{"three chars reach 9 bytes but only 3 runes", strings.Repeat("€", 3), true},
{"72 bytes", strings.Repeat("a", 72), false},
{"73 bytes exceeds the bcrypt cap", strings.Repeat("a", 73), true},
} {
t.Run(tc.name, func(t *testing.T) {
if got := validatePassword(tc.pw); (got != "") != tc.wantErr {
t.Fatalf("validatePassword(%q)=%q, wantErr=%v", tc.pw, got, tc.wantErr)
}
})
}
}