Skip to content

fix(validate): stop leaking raw error details in the non-ZodError fallback (+ extend regression test to middleware/)#154

Merged
CryptoJones merged 1 commit into
masterfrom
fix/validate-middleware-error-leak
May 19, 2026
Merged

fix(validate): stop leaking raw error details in the non-ZodError fallback (+ extend regression test to middleware/)#154
CryptoJones merged 1 commit into
masterfrom
fix/validate-middleware-error-leak

Conversation

@CryptoJones
Copy link
Copy Markdown
Owner

Closes #153.

Summary

fmt(err) in app/middleware/validate.js had the same error: String(err) shape #140 scrubbed from every controller — but #140's regression test (tests/unit/controller-error-shape.test.js) only walked app/controllers/, so this middleware copy survived. The non-ZodError fallback branch fires defensively when schema.safeParse surfaces something zod didn't wrap; when it does, the raw stringified error lands in the 400 response body.

Two changes:

  1. Drop error: String(err) from the validate fallback. Clients see { message: 'Validation failed.' }; the original error stays in operator-side logs.
  2. Extend the regression test to scan app/middleware/ in addition to app/controllers/, broaden the pattern set (catches String(err) / err.message / error.message variants), and widen the matched status-code range from 5xx to 4xx-or-5xx so this validation-400 case is in scope.

Test plan

  • npm run lint — clean
  • npm test — 604 passed (was 595 + 9 new = 8 per-middleware-file assertions + 1 middleware-dir sanity-floor), 15 skipped
  • The previous regression test still passes against the (already-clean) controllers/

Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/

…lback (+ extend regression test to middleware/)

The `fmt(err)` helper in `app/middleware/validate.js` had the same
`error: String(err)` pattern I scrubbed from all 17 controllers in
#140 — but the regression test from that PR only walked
`app/controllers/`, so the middleware copy of the leak survived.

The fallback branch fires when `schema.safeParse` somehow surfaces
a non-ZodError (defensive — zod normally always wraps). When it
does, the raw stringified error landed in the 400 response body.
A future zod release or a quirky schema mutation could trip it,
leaking internal text the global error-handler policy explicitly
suppresses.

Two changes:

1. validate.js: drop `error: String(err)` from the fallback shape.
   The client gets a generic `{ message: 'Validation failed.' }`;
   the original error is whatever caller logging propagates.
2. tests/unit/controller-error-shape.test.js: scan
   `app/middleware/` too, not just `app/controllers/`. Future drift
   in either directory now fails CI. Also broadens the pattern set
   to catch `String(err)` (singular variable) and `error.message` /
   `err.message`, plus widens the status-code match from 5xx to
   4xx-or-5xx so this validation 400 case is in scope.

Net: 604 tests pass (was 595 + 9 new = 8 per-middleware-file
checks + 1 sanity floor for the middleware directory).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CryptoJones CryptoJones merged commit 53b408a into master May 19, 2026
3 checks passed
@CryptoJones CryptoJones deleted the fix/validate-middleware-error-leak branch May 19, 2026 07:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validate: error: String(err) leak in the non-ZodError fallback (regression sibling of #139)

1 participant