fix(portal): GRO-2203 validate petId as UUID before PATCH lookup (500→404) #177

Merged
Flea Flicker merged 1 commits from fix/gro-2203-portal-pet-patch-uuid-validation into dev 2026-06-08 17:03:45 +00:00
Member

GRO-2203 — Portal PATCH /api/portal/pets/:petId returns 500 on malformed (non-UUID) petId

Problem

portalRouter.patch("/pets/:petId", …) passed the raw petId param straight into where(eq(pets.id, petId)). A non-UUID string made Postgres throw invalid input syntax for type uuid, surfacing as an unhandled 500 Internal Server Error. Expected a clean typed error (400/404).

Fix

Validate petId with z.string().uuid() before the DB lookup and return the existing 404 {"error":"Not found"} for malformed ids. This mirrors the established GRO-2014 guard in src/routes/pets.ts. The valid-UUID-not-found case already returned 404, so the malformed case now matches it.

Changes

  • src/routes/portal.ts — UUID guard in the PATCH handler before db.select().
  • src/__tests__/portalPets.test.ts — regression test: non-UUID petId → 404, no mutation persisted.
  • UAT_PLAYBOOK.md — added §8 TC-API-8.16 (malformed petId → 404).

Verification

  • vitest run src/__tests__/portalPets.test.ts → 10/10 pass (new test included).
  • npm run typecheck → clean.
  • npm run lint → 0 errors (7 pre-existing warnings, none in touched files).

Severity

Low robustness/hardening item. Requires an authenticated portal session; web client only sends real UUIDs, so no happy-path impact. No data leak in the prior 500 body.

Closes GRO-2203.

Co-Authored-By: Paperclip noreply@paperclip.ing

## GRO-2203 — Portal PATCH `/api/portal/pets/:petId` returns 500 on malformed (non-UUID) petId ### Problem `portalRouter.patch("/pets/:petId", …)` passed the raw `petId` param straight into `where(eq(pets.id, petId))`. A non-UUID string made Postgres throw `invalid input syntax for type uuid`, surfacing as an unhandled **500 Internal Server Error**. Expected a clean typed error (400/404). ### Fix Validate `petId` with `z.string().uuid()` before the DB lookup and return the existing `404 {"error":"Not found"}` for malformed ids. This mirrors the established GRO-2014 guard in `src/routes/pets.ts`. The valid-UUID-not-found case already returned 404, so the malformed case now matches it. ### Changes - `src/routes/portal.ts` — UUID guard in the PATCH handler before `db.select()`. - `src/__tests__/portalPets.test.ts` — regression test: non-UUID `petId` → 404, no mutation persisted. - `UAT_PLAYBOOK.md` — added **§8 TC-API-8.16** (malformed petId → 404). ### Verification - `vitest run src/__tests__/portalPets.test.ts` → 10/10 pass (new test included). - `npm run typecheck` → clean. - `npm run lint` → 0 errors (7 pre-existing warnings, none in touched files). ### Severity Low robustness/hardening item. Requires an authenticated portal session; web client only sends real UUIDs, so no happy-path impact. No data leak in the prior 500 body. Closes GRO-2203. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Flea Flicker added 1 commit 2026-06-08 17:01:50 +00:00
fix(portal): GRO-2203 validate petId as UUID before PATCH lookup (500→404)
CI / Test (pull_request) Successful in 23s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Images (pull_request) Successful in 1m2s
bd9866520b
A non-UUID :petId passed straight into where(eq(pets.id, petId)) made
Postgres throw "invalid input syntax for type uuid", surfacing as an
unhandled 500. Guard the param with z.string().uuid() and return the
existing 404 {"error":"Not found"} for malformed ids, mirroring the
GRO-2014 fix in pets.ts. Valid-UUID-not-found already returned 404.

- Add regression test (non-UUID petId → 404, no mutation)
- Update UAT_PLAYBOOK.md §8 (TC-API-8.16)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Flea Flicker merged commit b842237425 into dev 2026-06-08 17:03:45 +00:00
Sign in to join this conversation.