v0.6.1: Code review bugfixes — 7 correctness/safety/quality fixes
- Fix http.NotFound(w, nil) → pass actual request in handlers
- Fix dashboard running/stopped counts to match displayed stacks
- Fix Secure cookie blocking HTTP login (dynamic based on request)
- Remove misleading subtle.ConstantTimeCompare in session check
- Fix cleanupSessions goroutine leak (proper ticker + done channel)
- Add http.MaxBytesReader (1MB) to API POST endpoints
- Cache time.LoadLocation("Europe/Budapest") in template funcmap
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,6 @@ package web
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
type session struct {
|
||||
token string
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
@@ -81,14 +79,15 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
token := s.createSession()
|
||||
isSecure := r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https"
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: sessionCookieName,
|
||||
Value: token,
|
||||
Path: "/",
|
||||
MaxAge: int(sessionMaxAge.Seconds()),
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Secure: isSecure,
|
||||
})
|
||||
|
||||
s.logger.Printf("[INFO] Login from %s", r.RemoteAddr)
|
||||
@@ -111,7 +110,7 @@ func (s *Server) createSession() string {
|
||||
token := hex.EncodeToString(b)
|
||||
|
||||
s.sessionsMu.Lock()
|
||||
s.sessions[token] = &session{token: token, expiresAt: time.Now().Add(sessionMaxAge)}
|
||||
s.sessions[token] = &session{expiresAt: time.Now().Add(sessionMaxAge)}
|
||||
s.sessionsMu.Unlock()
|
||||
|
||||
return token
|
||||
@@ -120,27 +119,35 @@ func (s *Server) createSession() string {
|
||||
func (s *Server) isValidSession(token string) bool {
|
||||
s.sessionsMu.RLock()
|
||||
defer s.sessionsMu.RUnlock()
|
||||
|
||||
sess, ok := s.sessions[token]
|
||||
if !ok || time.Now().After(sess.expiresAt) {
|
||||
return false
|
||||
}
|
||||
return subtle.ConstantTimeCompare([]byte(sess.token), []byte(token)) == 1
|
||||
return ok && time.Now().Before(sess.expiresAt)
|
||||
}
|
||||
|
||||
func (s *Server) cleanupSessions() {
|
||||
for range time.Tick(15 * time.Minute) {
|
||||
s.sessionsMu.Lock()
|
||||
now := time.Now()
|
||||
for t, sess := range s.sessions {
|
||||
if now.After(sess.expiresAt) {
|
||||
delete(s.sessions, t)
|
||||
ticker := time.NewTicker(15 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-s.done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.sessionsMu.Lock()
|
||||
now := time.Now()
|
||||
for t, sess := range s.sessions {
|
||||
if now.After(sess.expiresAt) {
|
||||
delete(s.sessions, t)
|
||||
}
|
||||
}
|
||||
s.sessionsMu.Unlock()
|
||||
}
|
||||
s.sessionsMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Close signals the server to stop background goroutines.
|
||||
func (s *Server) Close() {
|
||||
close(s.done)
|
||||
}
|
||||
|
||||
func (s *Server) renderLogin(w http.ResponseWriter, errorMsg string) {
|
||||
data := map[string]interface{}{
|
||||
"Title": "Bejelentkezés",
|
||||
|
||||
Reference in New Issue
Block a user