diff --git a/.convoys/redesign-scanner-flow/audit-redesign-scanner-flow-44.md b/.convoys/redesign-scanner-flow/audit-redesign-scanner-flow-44.md index c43e1fc..51a6142 100644 --- a/.convoys/redesign-scanner-flow/audit-redesign-scanner-flow-44.md +++ b/.convoys/redesign-scanner-flow/audit-redesign-scanner-flow-44.md @@ -24,8 +24,8 @@ Reports posted to [PR #44](https://github.com/varutasu/tcg-vault/pull/44#issueco | Slug | Priority | Source | | --- | --- | --- | -| `scanner-redesign-a11y-fixes` | P1 | **In progress** — PR pending | -| `scanner-user-cards-quantity-guard` | P2 | Reviewer — parseInt/NaN guard parity with decks handler | +| `scanner-redesign-a11y-fixes` | P1 | **RESOLVED** — PR #45 | +| `scanner-user-cards-quantity-guard` | P2 | **In progress** — PR pending | | `test-scanner-redesign-surfaces` | P2 | Reviewer — unit tests for new components + upload route | | `document-condition-foil-destination-semantics` | P3 | Reviewer — clarify or migrate condition/foil for collection/deck rows | | `camera-scanner-token-cleanup` | P3 | Design system — replace hardcoded hex overlay colors in CameraScanner | diff --git a/.convoys/scanner-user-cards-quantity-guard.md b/.convoys/scanner-user-cards-quantity-guard.md new file mode 100644 index 0000000..1407c57 --- /dev/null +++ b/.convoys/scanner-user-cards-quantity-guard.md @@ -0,0 +1,25 @@ +--- +name: scanner-user-cards-quantity-guard +classification: fix +success_metric: | + POST /api/user-cards rejects non-numeric and sub-1 quantity with 400, matching + the decks/[id]/cards handler contract. +depends_on: + - redesign-scanner-flow +status: open +created: 2026-05-27 +--- + +# Convoy: scanner-user-cards-quantity-guard + +P2 follow-up from `audit-redesign-scanner-flow-44` reviewer report. + +## Scope + +- `pages/api/user-cards.js` — `parseInt(quantity, 10)` + NaN / `< 1` guard on POST + +## Acceptance criteria + +1. `quantity: "abc"` → 400 `Quantity must be at least 1` +2. `quantity: 0` → 400 +3. Valid integer ≥ 1 uses parsed value for INSERT and UPDATE increment diff --git a/pages/api/user-cards.js b/pages/api/user-cards.js index 6739290..eb579d6 100644 --- a/pages/api/user-cards.js +++ b/pages/api/user-cards.js @@ -21,6 +21,11 @@ export default async function handler(req, res) { return res.status(400).json({ error: 'Card ID is required' }); } + const parsedQuantity = parseInt(quantity, 10); + if (Number.isNaN(parsedQuantity) || parsedQuantity < 1) { + return res.status(400).json({ error: 'Quantity must be at least 1' }); + } + const scanImageUrl = typeof scanImageUrlRaw === 'string' && scanImageUrlRaw.trim().length > 0 ? scanImageUrlRaw.trim() @@ -34,7 +39,7 @@ export default async function handler(req, res) { if (existingResult.rows.length > 0) { // Update quantity; preserve existing scan image unless a new URL is supplied - const newQuantity = existingResult.rows[0].quantity + quantity; + const newQuantity = existingResult.rows[0].quantity + parsedQuantity; await sql` UPDATE user_cards SET quantity = ${newQuantity}, @@ -46,7 +51,7 @@ export default async function handler(req, res) { // Insert new record await sql` INSERT INTO user_cards (user_id, card_id, quantity, condition, is_foil, scan_image_url) - VALUES (${user.userId}, ${cardId}, ${quantity}, ${condition}, ${is_foil}, ${scanImageUrl}) + VALUES (${user.userId}, ${cardId}, ${parsedQuantity}, ${condition}, ${is_foil}, ${scanImageUrl}) `; }