Files
deploy-felhom-compose/controller/internal/setup/csrf.go
T
admin 45f75a916c fix: P2+P3 bug fixes, hardening, and cleanup (18 files)
Bug fixes:
- Add applyEnvOverrides to LoadFromBytes (M05)
- Set state=failed on compose-up failure in selfupdate (M16)
- Clamp usableMB to min 0 in memory check (M22)
- Remove "manual" schedule from triggerAllCrossBackups (M23)
- Add mmcblk device handling for partition paths (M21)
- Fix stripPartition for mmcblk devices (L25)
- Fix TruncateStr for UTF-8 and negative maxLen (L05/L06)
- Fix AllDone to return false for empty restore plans (L14)
- Fix PushOnce to return actual errors (L39)
- Restore pending events on save failure in DrainPendingEvents (M03)
- Add duplicate check in AddStoragePath (M04)
- Call CleanupTempMounts after drive scan (H13)
- Log SetStep save errors (M25)

Hardening:
- Guard scheduler Start() against double-start (M14)
- Acquire mutex in scheduler Stop() before reading cancel (L24)
- Cap log lines parameter to 10000 (L31)
- Require POST for logout (L32)
- Use sync.Once for Server.Close() (L49)
- Panic on crypto/rand.Read failure in setup CSRF (L40)
- Validate Bearer token against Hub API key in CSRF (H16 fix)
- Replace custom hasPrefix with strings.HasPrefix (L13)
- Replace simpleHash with crc32.ChecksumIEEE (L48)

Cleanup:
- Remove dead imageName function (L02)
- Remove dead detectHostIPViaRoute function (L03)
- Rename shadowed copy variable to cp (L07)
- Copy DefaultEnabledEvents in GetNotificationPrefs early return (L09)
- Update BUGHUNT.md with comprehensive audit results

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:47:52 +01:00

54 lines
1.3 KiB
Go

package setup
import (
"crypto/rand"
"encoding/hex"
"net/http"
)
const csrfCookieName = "felhom_csrf"
const csrfFormField = "_csrf"
// generateCSRFToken creates a random 32-byte hex token.
func generateCSRFToken() string {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
panic("crypto/rand.Read failed: " + err.Error())
}
return hex.EncodeToString(b)
}
// setCSRFCookie sets the CSRF cookie on the response.
func setCSRFCookie(w http.ResponseWriter, token string) {
http.SetCookie(w, &http.Cookie{
Name: csrfCookieName,
Value: token,
Path: "/",
SameSite: http.SameSiteStrictMode,
HttpOnly: false, // JavaScript needs to read it for AJAX if needed
})
}
// validateCSRF checks that the form field matches the cookie.
func validateCSRF(r *http.Request) bool {
cookie, err := r.Cookie(csrfCookieName)
if err != nil || cookie.Value == "" {
return false
}
formToken := r.FormValue(csrfFormField)
if formToken == "" {
return false
}
return cookie.Value == formToken
}
// ensureCSRFToken returns the existing CSRF token from the cookie, or generates a new one.
func ensureCSRFToken(w http.ResponseWriter, r *http.Request) string {
if cookie, err := r.Cookie(csrfCookieName); err == nil && cookie.Value != "" {
return cookie.Value
}
token := generateCSRFToken()
setCSRFCookie(w, token)
return token
}