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 <noreply@anthropic.com>
This commit is contained in:
Harry Culpan 2026-06-30 18:31:18 -04:00
parent 3684baa2f5
commit c39bd3ec53

View file

@ -3,12 +3,20 @@ package data
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"os" "os"
"sync"
) )
var sessionMu sync.RWMutex
func ReadSessionData() (Session, error) { func ReadSessionData() (Session, error) {
var session Session var session Session
sessionMu.RLock()
data, err := os.ReadFile("players.json") data, err := os.ReadFile("players.json")
sessionMu.RUnlock()
if err != nil { if err != nil {
return session, err return session, err
} }
@ -16,7 +24,7 @@ func ReadSessionData() (Session, error) {
if err == nil { if err == nil {
for i, p := range session.Players { 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 { func PersistSession(session Session) error {
// Convert the object to JSON
jsonData, err := json.MarshalIndent(session, "", " ") jsonData, err := json.MarshalIndent(session, "", " ")
if err != nil { if err != nil {
return err return err
} }
// Write JSON data to file sessionMu.Lock()
err = os.WriteFile("players.json", jsonData, 0644) err = os.WriteFile("players.json", jsonData, 0644)
sessionMu.Unlock()
return err return err
} }