- Deck names: HTML-stripped + trimmed + max 200 chars (enforced in API routes)
- Deck import: card names HTML-stripped, quantity clamped 1–99, max 500 lines
- Scryfall JSON API: Called directly from the browser for search/collection where applicable
- Scryfall card images: Proxied via
GET /api/proxy-card-image?url=…for print/proxy export only — browserfetch()tocards.scryfall.iois blocked by CORS; the route only allowshttps://cards.scryfall.io/*(no open SSRF) - Commander Spellbook: Proxied via
/api/combos(server-side) to avoid CORS fragility - EDHREC: If added in future phases, MUST be proxied via a Next.js API route — do not call directly from browser
⚠️ CRITICAL: Never expose LLM API keys client-side.
When adding AI-assisted deck building:
- Store the API key in an environment variable (e.g.
OPENAI_API_KEY,ANTHROPIC_API_KEY) - Create a server-side API route:
POST /api/ai/suggest - The route receives the deck state, calls the LLM, returns suggestions
- The client NEVER sees the API key — it only calls
/api/ai/suggest
React escapes all rendered values by default. Do NOT use dangerouslySetInnerHTML with any user-controlled or Scryfall-sourced data.
The Spellbook proxy only forwards whitelisted query parameters (cards, format) with a 500-char cap. The card-image proxy validates the url parameter is exactly https://cards.scryfall.io/… before fetching. Never forward arbitrary user-controlled URLs to server-side fetch calls.