From b47721a02d3fdfb1b6a565df29c85e7c51d8c490 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Sat, 14 Feb 2026 11:02:38 -0800 Subject: feat: add secure_cookies configuration option\n\n- Added SecureCookies bool field to config.Settings\n- Added --secure-cookies command line flag\n- Updated CSRFMiddleware to use config setting instead of hardcoded value\n- Default is false for local development, set to true for production HTTPS\n- Updated config.example and README.md with documentation\n- Updated tests to pass config to CSRFMiddleware\n\nThis allows users to easily switch between insecure cookies (for local dev)\nand secure cookies (for production HTTPS) via config file or command line. --- README.md | 4 ++++ cmd/neko/main.go | 8 +++++++- config.example | 1 + config/config.go | 1 + web/web.go | 6 +++--- web/web_test.go | 3 ++- 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cfaf43f..362f78c 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,8 @@ Usage of neko: minutes between crawling feeds -p, --password string password to access web interface + --secure-cookies + set Secure flag on cookies (requires HTTPS) -u, --update fetch feeds and store new items -v, --verbose @@ -242,6 +244,7 @@ A subset of the command line options are supported in the configuration file, wi * imageproxy * minutes * password + * secure_cookies For example -- @@ -251,6 +254,7 @@ http: 9001 imageproxy: true minutes: 90 password: VeryLongRandomStringBecauseSecurityIsFun +# secure_cookies: true # Set to true when using HTTPS in production ``` diff --git a/cmd/neko/main.go b/cmd/neko/main.go index 47385b1..106a22e 100644 --- a/cmd/neko/main.go +++ b/cmd/neko/main.go @@ -27,7 +27,7 @@ func main() { } func Run(args []string) error { - var help, update, verbose, proxyImages bool + var help, update, verbose, proxyImages, secureCookies bool var configFile, dbfile, newFeed, export, password string var port, minutes int @@ -63,6 +63,8 @@ func Run(args []string) error { f.BoolVar(&proxyImages, "imageproxy", false, "rewrite and proxy all image requests") f.BoolVar(&proxyImages, "i", false, "rewrite and proxy all image requests (short)") + f.BoolVar(&secureCookies, "secure-cookies", false, "set Secure flag on cookies (requires HTTPS)") + f.BoolVar(&verbose, "verbose", false, "verbose output") f.BoolVar(&verbose, "v", false, "verbose output (short)") @@ -110,6 +112,10 @@ func Run(args []string) error { config.Config.ProxyImages = proxyImages } + if secureCookies != false { + config.Config.SecureCookies = secureCookies + } + models.InitDB() if update { diff --git a/config.example b/config.example index 8aa0ed3..b302cf0 100644 --- a/config.example +++ b/config.example @@ -3,3 +3,4 @@ http: 9001 imageproxy: true minutes: 90 password: VeryLongRandomStringBecauseSecurityIsFun +# secure_cookies: true # Set to true when using HTTPS in production diff --git a/config/config.go b/config/config.go index 32e4b07..ad7ebd1 100644 --- a/config/config.go +++ b/config/config.go @@ -12,6 +12,7 @@ type Settings struct { DigestPassword string `yaml:"password"` CrawlMinutes int `yaml:"minutes"` ProxyImages bool `yaml:"imageproxy"` + SecureCookies bool `yaml:"secure_cookies"` } var Config Settings diff --git a/web/web.go b/web/web.go index d8dd832..892def3 100644 --- a/web/web.go +++ b/web/web.go @@ -265,7 +265,7 @@ func NewRouter(cfg *config.Settings) http.Handler { // Removed default root handler for legacy UI - return SecurityHeadersMiddleware(CSRFMiddleware(mux)) + return SecurityHeadersMiddleware(CSRFMiddleware(cfg, mux)) } func Serve(cfg *config.Settings) { @@ -362,7 +362,7 @@ func generateRandomToken(n int) string { return hex.EncodeToString(b) } -func CSRFMiddleware(next http.Handler) http.Handler { +func CSRFMiddleware(cfg *config.Settings, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("csrf_token") var token string @@ -374,7 +374,7 @@ func CSRFMiddleware(next http.Handler) http.Handler { Path: "/", HttpOnly: false, // accessible by JS SameSite: http.SameSiteNoneMode, - Secure: false, // Set to true in production with HTTPS + Secure: cfg.SecureCookies, }) } else { token = cookie.Value diff --git a/web/web_test.go b/web/web_test.go index c6cf306..0cd2764 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -737,7 +737,8 @@ func TestGzipMiddlewareNonCompressible(t *testing.T) { } func TestCSRFMiddleware(t *testing.T) { - handler := CSRFMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + cfg := &config.Settings{SecureCookies: false} + handler := CSRFMiddleware(cfg, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) -- cgit v1.2.3