From 29e12d4db463e675919c3c242d7384e83d8524c6 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 11 Mar 2026 03:55:38 +0100 Subject: [PATCH 1/4] Update module code.forgejo.org/go-chi/session to v1.0.3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9a70251ad0..56ecc4491f 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( code.forgejo.org/go-chi/binding v1.0.1 code.forgejo.org/go-chi/cache v1.0.1 code.forgejo.org/go-chi/captcha v1.0.2 - code.forgejo.org/go-chi/session v1.0.2 + code.forgejo.org/go-chi/session v1.0.3 code.gitea.io/sdk/gitea v0.21.0 code.superseriousbusiness.org/exif-terminator v0.11.1 code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 diff --git a/go.sum b/go.sum index 380048714d..ab757aa052 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ code.forgejo.org/go-chi/cache v1.0.1 h1:w6IsDcPbeEnEYZn7M2HJe3/3/Ehtcw/72VjcVK7+ code.forgejo.org/go-chi/cache v1.0.1/go.mod h1:K3aQSyRIN4xiuqV1kanfQ6O4ToDpzDpY3bNOyGjFe3U= code.forgejo.org/go-chi/captcha v1.0.2 h1:vyHDPXkpjDv8bLO9NqtWzZayzstD/WpJ5xwEkAaqZGQ= code.forgejo.org/go-chi/captcha v1.0.2/go.mod h1:lxiPLcJ76UCZHoH31/Wbum4GUi2NgjfFZLrJkKv1lLE= -code.forgejo.org/go-chi/session v1.0.2 h1:pG+AXre9L9VXJmTaADXkmeEPuRalhmBXyv6tG2Rvjcc= -code.forgejo.org/go-chi/session v1.0.2/go.mod h1:HnEGyBny7WPzCiVLP2vzL5ssma+3gCSl/vLpuVNYrqc= +code.forgejo.org/go-chi/session v1.0.3 h1:ByJ9c/UC0AU57hxiGl53TXh+NdBOBwK/bhZ9jyadEwE= +code.forgejo.org/go-chi/session v1.0.3/go.mod h1:xzGtFrV/agCJoZCUhFDlqAr1he6BrAdqlaprKOB1W90= code.forgejo.org/xorm/xorm v1.3.9-forgejo.8 h1:dsSKm2nus0NhHsqYxeuB3Gldk6TtlusD1CBGV6V1SS0= code.forgejo.org/xorm/xorm v1.3.9-forgejo.8/go.mod h1:A7sFd3BFmRp20h6drSsCXgQRQdF8Vz8HuCSrzFS3m90= code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4= From 31fff54e17747a88aabf1d0defefe6c089d148c2 Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Fri, 13 Feb 2026 14:26:17 +0100 Subject: [PATCH 2/4] Improvement: Do not set session cookie for empty session This is based on https://code.forgejo.org/go-chi/session/pulls/80. The remainder of this message is largely copied from there: For interoperability with reverse proxies and CDNs, setting a session cookie for no good reason (login is a good reason) is a PITA, because it makes caching of content for anonymous (not logged-in) users very hard, requiring all kinds of special casing and error prone workarounds. In particular in an age of exploitative AI bot crawling, being able to serve content for anonymous users from a fast, efficient page cache is an important option. This patch lays a foundation by using an option added to go-chi/session to not create session cookies always, but rather only when the respective session is non-empty. Test cases are included there and omitted here. --- modules/session/db.go | 5 +++++ modules/session/redis.go | 5 +++++ modules/session/virtual.go | 5 +++++ routers/common/middleware.go | 1 + 4 files changed, 16 insertions(+) diff --git a/modules/session/db.go b/modules/session/db.go index eea7e2136e..57f658dfd6 100644 --- a/modules/session/db.go +++ b/modules/session/db.go @@ -84,6 +84,11 @@ func (s *DBStore) Flush() error { return nil } +// True if no keys have been set +func (s *DBStore) Empty() bool { + return len(s.data) == 0 +} + // DBProvider represents a DB session provider implementation. type DBProvider struct { maxLifetime int64 diff --git a/modules/session/redis.go b/modules/session/redis.go index cf84ef21d9..1e8c61da8b 100644 --- a/modules/session/redis.go +++ b/modules/session/redis.go @@ -103,6 +103,11 @@ func (s *RedisStore) Flush() error { return nil } +// True if no keys have been set +func (s *RedisStore) Empty() bool { + return len(s.data) == 0 +} + // RedisProvider represents a redis session provider implementation. type RedisProvider struct { c nosql.RedisClient diff --git a/modules/session/virtual.go b/modules/session/virtual.go index 1986ba64ad..cde9e60c4d 100644 --- a/modules/session/virtual.go +++ b/modules/session/virtual.go @@ -195,3 +195,8 @@ func (s *VirtualStore) Flush() error { s.data = make(map[any]any) return nil } + +// True if no keys have been set +func (s *VirtualStore) Empty() bool { + return len(s.data) == 0 +} diff --git a/routers/common/middleware.go b/routers/common/middleware.go index 7bc4890a43..5c8283e247 100644 --- a/routers/common/middleware.go +++ b/routers/common/middleware.go @@ -118,5 +118,6 @@ func Sessioner() func(next http.Handler) http.Handler { Secure: setting.SessionConfig.Secure, SameSite: setting.SessionConfig.SameSite, Domain: setting.SessionConfig.Domain, + DeferSetCookie: true, }) } From ea9f2a236bbe5aa804ef2ebd951b27ae0f863201 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 19 Mar 2026 02:18:52 +0100 Subject: [PATCH 3/4] fix: only destroy session if exists The virtual session doesn't unconditionally call `Read` of the provider, which means it's possible for a session to not exists (created by the call to `Read`). To avoid that the call to `Destroy` fails with that the session does not exists, do also the exists check for `Destroy`. --- modules/session/virtual.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/session/virtual.go b/modules/session/virtual.go index cde9e60c4d..fea7d11a44 100644 --- a/modules/session/virtual.go +++ b/modules/session/virtual.go @@ -76,7 +76,10 @@ func (o *VirtualSessionProvider) Exist(sid string) bool { func (o *VirtualSessionProvider) Destroy(sid string) error { o.lock.Lock() defer o.lock.Unlock() - return o.provider.Destroy(sid) + if o.provider.Exist(sid) { + return o.provider.Destroy(sid) + } + return nil } // Regenerate regenerates a session store from old session ID to new one. From dfbd6e50697722a38305b9d2829dd1caf3692061 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 20 Mar 2026 07:00:48 +0100 Subject: [PATCH 4/4] chore: update ingration test No session ID is even send now --- tests/integration/create_no_session_test.go | 22 +++++---------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/tests/integration/create_no_session_test.go b/tests/integration/create_no_session_test.go index 375add7014..fc9c38cabd 100644 --- a/tests/integration/create_no_session_test.go +++ b/tests/integration/create_no_session_test.go @@ -6,7 +6,6 @@ package integration import ( "net/http" "net/http/httptest" - "os" "path/filepath" "testing" @@ -31,8 +30,9 @@ func getSessionID(t *testing.T, resp *httptest.ResponseRecorder) string { found = true } } - assert.True(t, found) - assert.NotEmpty(t, sessionID) + if found { + assert.NotEmpty(t, sessionID) + } return sessionID } @@ -40,18 +40,6 @@ func sessionFile(tmpDir, sessionID string) string { return filepath.Join(tmpDir, sessionID[0:1], sessionID[1:2], sessionID) } -func sessionFileExist(t *testing.T, tmpDir, sessionID string) bool { - sessionFile := sessionFile(tmpDir, sessionID) - _, err := os.Lstat(sessionFile) - if err != nil { - if os.IsNotExist(err) { - return false - } - require.NoError(t, err) - } - return true -} - func TestSessionFileCreation(t *testing.T) { defer tests.PrepareTestEnv(t)() defer test.MockProtect(&setting.SessionConfig.ProviderConfig)() @@ -82,7 +70,7 @@ func TestSessionFileCreation(t *testing.T) { sessionID := getSessionID(t, resp) // We're not logged in so there should be no session - assert.False(t, sessionFileExist(t, tmpDir, sessionID)) + assert.Empty(t, sessionID) }) t.Run("CreateSessionOnLogin", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -92,7 +80,7 @@ func TestSessionFileCreation(t *testing.T) { sessionID := getSessionID(t, resp) // We're not logged in so there should be no session - assert.False(t, sessionFileExist(t, tmpDir, sessionID)) + assert.Empty(t, sessionID) req = NewRequestWithValues(t, "POST", "/user/login", map[string]string{ "user_name": "user2",