package main import ( "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" ) // 1283/AC1: a request whose Host is not mail..internal — a public name, an IP, or none — is answered // 404, naming nothing of the mail. func TestServerAnswers404ForAnyHostButItsOwnInternalName(t *testing.T) { s := NewServer(InternalHost("g14"), nil) for _, host := range []string{"example.com", "203.0.113.5", "", "mail.other-node.internal"} { w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/", nil) r.Host = host s.ServeHTTP(w, r) if w.Code != http.StatusNotFound { t.Fatalf("host %q: status %d, want 404", host, w.Code) } if strings.Contains(strings.ToLower(w.Body.String()), "mail") { t.Fatalf("host %q: the 404 body names mail: %s", host, w.Body.String()) } } } // Its own internal name is answered (not 404). func TestServerAnswersItsOwnInternalName(t *testing.T) { s := NewServer(InternalHost("g14"), nil) w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/", nil) r.Host = InternalHost("g14") s.ServeHTTP(w, r) if w.Code != http.StatusOK { t.Fatalf("status %d, want 200", w.Code) } } // A Host with a port still matches, port and all (the mesh's own listen carries one). func TestServerAnswersItsOwnInternalNameWithAPort(t *testing.T) { s := NewServer(InternalHost("g14"), nil) w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/", nil) r.Host = InternalHost("g14") + ":8099" s.ServeHTTP(w, r) if w.Code != http.StatusOK { t.Fatalf("status %d, want 200", w.Code) } } // 1283/AC2: every answer (200 and 404 alike) carries the content security policy. func TestServerSetsTheContentSecurityPolicyOnEveryAnswer(t *testing.T) { s := NewServer(InternalHost("g14"), nil) cases := []string{"example.com", InternalHost("g14")} for _, host := range cases { w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/", nil) r.Host = host s.ServeHTTP(w, r) if got := w.Header().Get("Content-Security-Policy"); got != CSP { t.Fatalf("host %q: CSP %q, want %q", host, got, CSP) } } } // 1283/AC3: open with nothing given answers the root; with account, folder and uid together, that mail's own // address. func TestOpenAnswersTheRootOrOneMailsAddress(t *testing.T) { b := &Bundle{Env: Env{Node: "g14"}} out, err := b.openTool(map[string]any{}) if err != nil { t.Fatal(err) } if got := out.(map[string]any)["url"]; got != "https://mail.g14.internal/" { t.Fatalf("root url: %v", got) } out, err = b.openTool(map[string]any{"account": "mesh", "folder": "INBOX", "uid": float64(7)}) if err != nil { t.Fatal(err) } url := out.(map[string]any)["url"].(string) if !strings.HasPrefix(url, "https://mail.g14.internal/mail?") || !strings.Contains(url, "account=mesh") || !strings.Contains(url, "folder=INBOX") || !strings.Contains(url, "uid=7") { t.Fatalf("mail url: %s", url) } if _, err := b.openTool(map[string]any{"account": "mesh"}); err == nil { t.Fatalf("account alone, with no folder or uid, was not refused") } } // 1283/AC4: with the working directory and home read-only, the server still starts and answers a page (it // writes nothing to disk). func TestServerStartsAndAnswersAPageWithACwdAndHomeReadOnly(t *testing.T) { ro := t.TempDir() if err := os.Chmod(ro, 0o500); err != nil { t.Fatal(err) } t.Cleanup(func() { _ = os.Chmod(ro, 0o700) }) oldWd, err := os.Getwd() if err != nil { t.Fatal(err) } oldHome := os.Getenv("HOME") t.Cleanup(func() { _ = os.Chdir(oldWd) _ = os.Setenv("HOME", oldHome) }) if err := os.Chdir(ro); err != nil { t.Fatal(err) } _ = os.Setenv("HOME", filepath.Join(ro, "home-does-not-exist")) s := NewServer(InternalHost("g14"), nil) srv := httptest.NewServer(s) defer srv.Close() req, err := http.NewRequest(http.MethodGet, srv.URL+"/", nil) if err != nil { t.Fatal(err) } req.Host = InternalHost("g14") resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("status %d, want 200", resp.StatusCode) } }