T09 · Insecure Skill Coding Practices
- Location
scripts/game_engine.py:226- Finding
Active-Game Export Discloses Secret Roles and Words
- Content
View full analysis
Vulnerability Details
File Location:
scripts/game_engine.py:226-231;scripts/db.py:482-519
Vulnerability Type: Missing lifecycle authorization check leading to confidential game-state disclosure
Risk Level: HighVulnerable Code
scripts/game_engine.py:226-231:python def cmd_export(game_id): """Export the game record as Markdown.""" md = db.export_game_record(game_id) if md is None: return {"error": f"Game not found: {game_id}"} return mdRelevant disclosure logic in
scripts/db.py:482-519:python def export_game_record(game_id, base_dir=None): """ Export the complete game record in Markdown format. """ status = get_game_status(game_id) if not status: return None config = status["config"] config_str = f"{config['civilian']} civilians/{config['undercover']} undercover" if config.get("white_board", 0) > 0: config_str += f"/{config['white_board']} blank slate" lines = [ "# Game Record", "", f"Time: {status['created_at'][:16].replace('T', ' ')}", f"Players: {', '.join(status['all_players'])}", f"Configuration: {config_str}", "", "---", "", ] lines.append("## Role Assignments") lines.append("") lines.append("| Player | Role | Word |") lines.append("|------|------|------|") for r in status["roles"]: lines.append(f"| {r['player']} | {r['role']} | {r['word']} |") lines.append(f"- Civilian word: “{status['civilian_word']}”") lines.append(f"- Undercover word: “{status['undercover_word']}”")Technical Analysis
The export command retrieves and renders the complete game state without verifying that the game has finished. The generated record contains every player's role and assigned word, as well as both secret words.
The documented workflow calls for ...[truncated 1605 chars]
- Remediation
View remediation
Remediation Suggestions
- Enforce the finished-state requirement inside
db.export_game_record:python game = get_game_by_id(game_id) if not game: return None if game["status"] != "finished": raise PermissionError("Complete records cannot be exported before the game ends") - Add the same check to
cmd_exportto provide a clear user-facing error, while retaining the database-layer check as the authoritative control. - If active-game exports are required, implement a separate redacted export function that excludes:
- Roles
- Assigned words
- Civilian and undercover words
- Individual vote targets
- Any other unrevealed game information
- Add automated tests proving that full export fails while
status == "playing"and succeeds only afterend_game. - Avoid treating Skill instructions as an authorization boundary; enforce all confidentiality restrictions in code.
- Enforce the finished-state requirement inside
