Defensive coding: sanitize free text, constrain inputs
Player-authored free text is scrubbed at the API boundary before storage via a new validation.py: drop control characters and unpaired surrogates, trim whitespace, and cap length (names 120, other text 2000). Applied to crew/player names, the character sheet, like/hate answers, techniques, recruit techniques, the renamed Name, the Gat description, and challenge stakes. Values matched against stored text (swap offers, face-card assignments) and identifiers/enums are left untouched so they still compare equal. Constrained set-role to the two real roles so it can't stash an arbitrary string in the column. Audited the rest: queries are parameterized by SQLModel (the lone f-string SQL in database.py is the hardcoded legacy-migration list, no user input); objective-type and rollback writes already whitelist their keys; no endpoint accepts a generic (field, value) write. Tests cover the sanitizer (control chars, surrogates, emoji, caps) and the create/join HTTP path end-to-end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ from pathlib import Path
|
||||
from . import crud
|
||||
from . import maintenance
|
||||
from .database import run_migrations, get_session, engine
|
||||
from .validation import sanitize_name
|
||||
from .ws import manager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -149,7 +150,7 @@ def create_game_route(
|
||||
crew_name: str = Form(...),
|
||||
db: Session = Depends(get_session)
|
||||
):
|
||||
game = crud.create_game(db, crew_name=crew_name.strip())
|
||||
game = crud.create_game(db, crew_name=sanitize_name(crew_name))
|
||||
# admin_key is only revealed here, to the creator; the state endpoint hides it
|
||||
return {"id": game.id, "admin_key": game.admin_key}
|
||||
|
||||
@@ -165,7 +166,7 @@ def join_game_submit(
|
||||
|
||||
# First player is automatically the creator
|
||||
is_creator = len(game.players) == 0
|
||||
player = crud.add_player(db, game_id, name.strip(), is_creator=is_creator)
|
||||
player = crud.add_player(db, game_id, sanitize_name(name), is_creator=is_creator)
|
||||
return {"id": player.id}
|
||||
|
||||
@api.post("/game/{game_id}/player/{player_id}/leave")
|
||||
|
||||
Reference in New Issue
Block a user