summaryrefslogtreecommitdiff
path: root/player-server/internal/api
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-20 14:10:49 +0300
committerPaul Buetow <paul@buetow.org>2026-05-20 14:10:49 +0300
commit560d8ac3efeccb4e667444dec822c2bd454cb587 (patch)
tree8e3b55156052dffc1341ce3cb67532e042e1f666 /player-server/internal/api
parentb70dfdb80d897abf77b74a78eb59b984dfba64d4 (diff)
parent7e65725253e40dce726d1be441b33b72d72ea8c4 (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.go14
-rw-r--r--player-server/internal/api/handlers_share.go10
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.