felt/internal/player/export.go
Mikkel Georgsen 8b4b131371 feat(01-07): add player API routes, ranking tests, CSV export safety
- PlayerHandler with all CRUD routes and tournament player operations
- Buy-in flow: register + financial engine + auto-seat suggestion
- Bust flow: hitman selection + bounty transfer + re-ranking
- Undo bust with full re-ranking and rankings response
- Rankings API endpoint returning derived positions
- QR code endpoint returns PNG image with Cache-Control header
- CSV import via multipart upload (admin only)
- Player merge endpoint (admin only)
- CSV export safety: formula injection neutralization (tab-prefix =,+,-,@)
- Ranking tests: bust order, undo re-ranking, early undo, re-entry,
  deal positions, auto-close, concurrent busts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 04:35:05 +01:00

46 lines
1.2 KiB
Go

package player
import (
"strings"
)
// SanitizeCSVField neutralizes potential formula injection in CSV output.
// When generating CSV, prefix any cell value starting with =, +, -, or @
// with a tab character to prevent spreadsheet formula injection when the
// CSV is opened in Excel/LibreOffice.
func SanitizeCSVField(value string) string {
if len(value) == 0 {
return value
}
switch value[0] {
case '=', '+', '-', '@':
return "\t" + value
}
return value
}
// SanitizeCSVRow sanitizes all fields in a CSV row.
func SanitizeCSVRow(fields []string) []string {
sanitized := make([]string, len(fields))
for i, f := range fields {
sanitized[i] = SanitizeCSVField(f)
}
return sanitized
}
// SanitizeCSVFields sanitizes a map of field names to values.
func SanitizeCSVFields(fields map[string]string) map[string]string {
sanitized := make(map[string]string, len(fields))
for k, v := range fields {
sanitized[k] = SanitizeCSVField(v)
}
return sanitized
}
// IsFormulaInjection checks if a string starts with a formula-injection character.
func IsFormulaInjection(value string) bool {
if len(value) == 0 {
return false
}
return strings.ContainsRune("=+-@", rune(value[0]))
}