aboutsummaryrefslogtreecommitdiffstats
path: root/web/login_test.go
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-16 15:36:49 -0800
committerGitHub <noreply@github.com>2026-02-16 15:36:49 -0800
commitc538ccd5cd1d6960c894a2969ee49317009dc9ed (patch)
tree1d1b1295f3d63df85fffadaa1159414c74399c6c /web/login_test.go
parentfeea1a814a212efd9b14b8f05e84104ace3c473a (diff)
parent80d132b1714aaa281a6532ff66892bfdb7d2c5c2 (diff)
downloadneko-c538ccd5cd1d6960c894a2969ee49317009dc9ed.tar.gz
neko-c538ccd5cd1d6960c894a2969ee49317009dc9ed.tar.bz2
neko-c538ccd5cd1d6960c894a2969ee49317009dc9ed.zip
Merge pull request #7 from adammathes/claude/thicket-ready-crank-RmQuI
More crank pulls from claude
Diffstat (limited to 'web/login_test.go')
-rw-r--r--web/login_test.go86
1 files changed, 86 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)
+ }
+}