diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-20 14:10:49 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-20 14:10:49 +0300 |
| commit | 560d8ac3efeccb4e667444dec822c2bd454cb587 (patch) | |
| tree | 8e3b55156052dffc1341ce3cb67532e042e1f666 /player-server/internal/api | |
| parent | b70dfdb80d897abf77b74a78eb59b984dfba64d4 (diff) | |
| parent | 7e65725253e40dce726d1be441b33b72d72ea8c4 (diff) | |
Merge j9+i9: LIKE wildcard escaping and CreateUser password validation
- escapeLike() helper in repository/media.go for LIKE search safety
- ErrWeakPassword sentinel in service.go (min 8 chars, HTTP 400)
- CreateUser rejects empty/short passwords in service/user.go
- ErrWeakPassword auto-dispatched via HTTPStatuser (no switch needed)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diffstat (limited to 'player-server/internal/api')
| -rw-r--r-- | player-server/internal/api/handlers_auth.go | 14 | ||||
| -rw-r--r-- | player-server/internal/api/handlers_share.go | 10 |
2 files changed, 22 insertions, 2 deletions
diff --git a/player-server/internal/api/handlers_auth.go b/player-server/internal/api/handlers_auth.go index 3771877..c770380 100644 --- a/player-server/internal/api/handlers_auth.go +++ b/player-server/internal/api/handlers_auth.go @@ -191,16 +191,26 @@ func (s *Server) handleReadyz(w http.ResponseWriter, r *http.Request) { } func (s *Server) setSessionCookie(w http.ResponseWriter, value string) { + sessionDuration := time.Duration(s.cfg.SessionTimeoutHours) * time.Hour http.SetCookie(w, &http.Cookie{ Name: "session", Value: value, Path: "/", HttpOnly: true, Secure: s.cfg.SecureCookies, - SameSite: http.SameSiteStrictMode, + // SameSite=Lax allows cross-context navigation (e.g. mobile webviews, + // embedded players following a link) while still blocking most CSRF + // vectors. SameSite=Strict would drop the cookie on any cross-site + // top-level navigation, causing unnecessary session loss. + SameSite: http.SameSiteLaxMode, + // MaxAge is the authoritative persistence signal in modern browsers; + // Expires is the legacy fallback. Both are set to the same session + // duration so the cookie persists across browser restarts regardless + // of which attribute the client honours. + MaxAge: int(sessionDuration.Seconds()), // Use the injected clock so tests can assert the cookie Expires // value deterministically (no flakiness from time.Now()). - Expires: s.clk.Now().Add(time.Duration(s.cfg.SessionTimeoutHours) * time.Hour), + Expires: s.clk.Now().Add(sessionDuration), }) } diff --git a/player-server/internal/api/handlers_share.go b/player-server/internal/api/handlers_share.go index 03ec32a..ccf1632 100644 --- a/player-server/internal/api/handlers_share.go +++ b/player-server/internal/api/handlers_share.go @@ -7,6 +7,7 @@ import ( "time" "codeberg.org/snonux/player/internal/service" + "codeberg.org/snonux/player/internal/web" ) // ------------------------------------------------------------------ @@ -91,6 +92,15 @@ func (s *Server) handleSharePage(w http.ResponseWriter, r *http.Request) { return } + // Sanitize the media filename before embedding it in the share page to + // prevent XSS via HTML injection and to cap memory growth from enormous + // filenames (DoS). SanitizeFileName truncates to MaxFileNameLength runes + // and HTML-escapes the result; encoding/json also Unicode-escapes </> + // inside string values, so both layers reinforce each other. + if res.Media != nil { + res.Media.FileName = web.SanitizeFileName(res.Media.FileName) + } + // Render the HTML view via the dedicated renderer. This keeps the // handler focused on transport concerns (status codes, headers) and // keeps templating in the internal/web package. |
