From c39bd3ec5317d5ef8a01ec54cdf1dfa7e7fa8f82 Mon Sep 17 00:00:00 2001 From: Harry Culpan Date: Tue, 30 Jun 2026 18:31:18 -0400 Subject: [PATCH] Add mutex to protect players.json and fix URL-encode player names Concurrent HTTP requests and the cron job could race on the file, causing lost updates or torn writes. A package-level RWMutex now guards all reads and writes. Also URL-encodes player names in ToggleUrl to prevent query-parameter injection from names containing '&' or '='. Co-Authored-By: Claude Sonnet 4.6 --- pkg/data/loaddata.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/data/loaddata.go b/pkg/data/loaddata.go index 8846977..8923116 100644 --- a/pkg/data/loaddata.go +++ b/pkg/data/loaddata.go @@ -3,12 +3,20 @@ package data import ( "encoding/json" "fmt" + "net/url" "os" + "sync" ) +var sessionMu sync.RWMutex + func ReadSessionData() (Session, error) { var session Session + + sessionMu.RLock() data, err := os.ReadFile("players.json") + sessionMu.RUnlock() + if err != nil { return session, err } @@ -16,7 +24,7 @@ func ReadSessionData() (Session, error) { if err == nil { for i, p := range session.Players { - session.Players[i].ToggleUrl = "/sessioninfo?playerName=" + p.Name + "&playerAttending=toggle" + session.Players[i].ToggleUrl = "/sessioninfo?playerName=" + url.QueryEscape(p.Name) + "&playerAttending=toggle" } } @@ -24,14 +32,14 @@ func ReadSessionData() (Session, error) { } func PersistSession(session Session) error { - // Convert the object to JSON jsonData, err := json.MarshalIndent(session, "", " ") if err != nil { return err } - // Write JSON data to file + sessionMu.Lock() err = os.WriteFile("players.json", jsonData, 0644) + sessionMu.Unlock() return err }