From 6534380f6702d6b880d93b5620faf2dfc42f77e3 Mon Sep 17 00:00:00 2001 From: KoalaTrapLord <265133236+KoalaTrapLord@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:40:28 -0400 Subject: [PATCH] fix: replace full-table scan with parameterized WHERE in _build_deck_report_from_db (closes #60) --- routes/coach.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/routes/coach.py b/routes/coach.py index 14878b5..bc956ac 100644 --- a/routes/coach.py +++ b/routes/coach.py @@ -261,21 +261,35 @@ def _build_deck_report_from_db(deck_slug: str): from coach.models import DeckReport, CardPerformance, DeckStructure conn = _get_db_conn() - # Find the deck by slug match against the deck name - rows = conn.execute( - "SELECT id, name, commander_name, color_identity FROM decks ORDER BY id" - ).fetchall() + # --- Input validation --- + import re + if not deck_slug or len(deck_slug) > 200: + return None + deck_slug = re.sub(r'[^a-zA-Z0-9 _-]', '', deck_slug) + if not deck_slug: + return None - matched_deck = None - for r in rows: - name = r["name"] or "" - slug = name.lower().replace(" ", "-") - # Also try a more thorough slugify - import re - clean_slug = re.sub(r'[^a-z0-9]+', '-', name.lower()).strip('-') - if slug == deck_slug.lower() or clean_slug == deck_slug.lower() or name.lower() == deck_slug.lower(): - matched_deck = r - break + # Find the deck by parameterized query instead of full-table scan + slug_lower = deck_slug.lower() + slug_hyphenated = slug_lower.replace(' ', '-') + clean_slug = re.sub(r'[^a-z0-9]+', '-', slug_lower).strip('-') + + if slug_lower.isdigit(): + # Numeric slug — match by ID directly + matched_deck = conn.execute( + "SELECT id, name, commander_name, color_identity FROM decks WHERE id = ?", + (int(slug_lower),) + ).fetchone() + else: + # Try exact name match, hyphenated match, or clean-slug match + matched_deck = conn.execute( + """SELECT id, name, commander_name, color_identity FROM decks + WHERE LOWER(name) = ? + OR LOWER(REPLACE(name, ' ', '-')) = ? + OR LOWER(REPLACE(name, ' ', '-')) = ? + LIMIT 1""", + (slug_lower, slug_hyphenated, clean_slug) + ).fetchone() if matched_deck is None: return None