diff options
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", |
