diff options
| author | Claude <noreply@anthropic.com> | 2026-02-16 23:31:54 +0000 |
|---|---|---|
| committer | Claude <noreply@anthropic.com> | 2026-02-16 23:31:54 +0000 |
| commit | 5f5f8be8c6ca78f5d61372544bb24d692d9597f0 (patch) | |
| tree | 7c864522e7c5aff3552d493136a692f6c167c528 /web | |
| parent | ed1b953e1336aeda282b46acd1c5e21529fa0950 (diff) | |
| download | neko-5f5f8be8c6ca78f5d61372544bb24d692d9597f0.tar.gz neko-5f5f8be8c6ca78f5d61372544bb24d692d9597f0.tar.bz2 neko-5f5f8be8c6ca78f5d61372544bb24d692d9597f0.zip | |
Add tests for CSRF exclusions, Filter includeContent, multi-feed, and routing
- CSRF: test excluded paths (/api/login, /api/logout), PUT/DELETE methods
- Item model: test Filter includeContent flag, ItemById returns content,
multiple feed_ids filtering
- API: test read_filter=all param, feed_ids comma-separated filter,
full_content exclusion from stream
- Routing: add v3 frontend route test
https://claude.ai/code/session_019Z4VJxzY7tcAuNkPAkvry9
Diffstat (limited to 'web')
| -rw-r--r-- | web/login_test.go | 86 | ||||
| -rw-r--r-- | web/routing_test.go | 7 |
2 files changed, 93 insertions, 0 deletions
diff --git a/web/login_test.go b/web/login_test.go index f4931b2..9d78396 100644 --- a/web/login_test.go +++ b/web/login_test.go @@ -85,3 +85,89 @@ func TestCSRFLoginWithFormToken(t *testing.T) { t.Errorf("Expected POST /other with valid CSRF header to succeed, got 403") } } + +func TestCSRFExcludedPaths(t *testing.T) { + originalPw := config.Config.DigestPassword + defer func() { config.Config.DigestPassword = originalPw }() + config.Config.DigestPassword = "secret" + + mux := http.NewServeMux() + mux.HandleFunc("/api/login", apiLoginHandler) + mux.HandleFunc("/api/logout", apiLogoutHandler) + handler := CSRFMiddleware(&config.Config, mux) + + // POST /api/login without CSRF token should succeed (excluded path) + req := httptest.NewRequest("POST", "/api/login", strings.NewReader("password=secret")) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + if rr.Code == http.StatusForbidden { + t.Error("POST /api/login should be excluded from CSRF protection") + } + + // POST /api/logout without CSRF token should succeed (excluded path) + req = httptest.NewRequest("POST", "/api/logout", nil) + rr = httptest.NewRecorder() + handler.ServeHTTP(rr, req) + if rr.Code == http.StatusForbidden { + t.Error("POST /api/logout should be excluded from CSRF protection") + } +} + +func TestCSRFPutAndDeleteMethods(t *testing.T) { + cfg := &config.Settings{SecureCookies: false} + inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + handler := CSRFMiddleware(cfg, inner) + + // GET to get a token + getReq := httptest.NewRequest("GET", "/", nil) + getRR := httptest.NewRecorder() + handler.ServeHTTP(getRR, getReq) + + var csrfToken string + for _, c := range getRR.Result().Cookies() { + if c.Name == "csrf_token" { + csrfToken = c.Value + } + } + + // PUT without token should fail + req := httptest.NewRequest("PUT", "/item/1", nil) + req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken}) + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusForbidden { + t.Errorf("PUT without CSRF token should return 403, got %d", rr.Code) + } + + // DELETE without token should fail + req = httptest.NewRequest("DELETE", "/feed/1", nil) + req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken}) + rr = httptest.NewRecorder() + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusForbidden { + t.Errorf("DELETE without CSRF token should return 403, got %d", rr.Code) + } + + // PUT with valid token should succeed + req = httptest.NewRequest("PUT", "/item/1", nil) + req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken}) + req.Header.Set("X-CSRF-Token", csrfToken) + rr = httptest.NewRecorder() + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusOK { + t.Errorf("PUT with valid CSRF token should succeed, got %d", rr.Code) + } + + // DELETE with valid token should succeed + req = httptest.NewRequest("DELETE", "/feed/1", nil) + req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken}) + req.Header.Set("X-CSRF-Token", csrfToken) + rr = httptest.NewRecorder() + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusOK { + t.Errorf("DELETE with valid CSRF token should succeed, got %d", rr.Code) + } +} diff --git a/web/routing_test.go b/web/routing_test.go index 972b208..a7c5c37 100644 --- a/web/routing_test.go +++ b/web/routing_test.go @@ -36,6 +36,13 @@ func TestRouting(t *testing.T) { containsBody: "<!doctype html>", }, { + name: "/v3/ serves v3 UI", + path: "/v3/", + method: "GET", + expectedStatus: http.StatusOK, + containsBody: "<!doctype html>", + }, + { name: "/v1/ redirects unauthenticated", path: "/v1/", method: "GET", |
