Problem
app/middleware/validate.js's fmt(err) helper has the same error: String(err) shape #140 scrubbed from every controller:
function fmt(err) {
if (err instanceof ZodError) { /* …structured … */ }
return { message: 'Validation failed.', error: String(err) };
}
The fallback branch fires when schema.safeParse surfaces a non-ZodError (defensive — zod normally always wraps, but a future zod release or unusual schema mutation could trip it). When it does, the raw stringified error lands in the 400 response body — same class of leak as #139, but in a middleware path the controller-only regression test from #140 doesn't cover.
The miss happened because tests/unit/controller-error-shape.test.js only walks app/controllers/, not app/middleware/. A real regression net should cover both.
Proposed fix
- Drop
error: String(err) from validate's fallback shape. Clients get { message: 'Validation failed.' }; the original error stays in operator-side logs via the normal logger paths.
- Extend
tests/unit/controller-error-shape.test.js to scan app/middleware/ in addition to app/controllers/, with the same test.each()-per-file structure. Future drift in either directory then fails CI immediately. Also broaden the regex set to catch String(err) (single-letter variable), err.message, and error.message, and widen the matched status-code range from 5xx to 4xx-or-5xx so this validation-400 case is in scope.
Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/
Problem
app/middleware/validate.js'sfmt(err)helper has the sameerror: String(err)shape #140 scrubbed from every controller:The fallback branch fires when
schema.safeParsesurfaces a non-ZodError(defensive — zod normally always wraps, but a future zod release or unusual schema mutation could trip it). When it does, the raw stringified error lands in the 400 response body — same class of leak as #139, but in a middleware path the controller-only regression test from #140 doesn't cover.The miss happened because
tests/unit/controller-error-shape.test.jsonly walksapp/controllers/, notapp/middleware/. A real regression net should cover both.Proposed fix
error: String(err)from validate's fallback shape. Clients get{ message: 'Validation failed.' }; the original error stays in operator-side logs via the normal logger paths.tests/unit/controller-error-shape.test.jsto scanapp/middleware/in addition toapp/controllers/, with the sametest.each()-per-file structure. Future drift in either directory then fails CI immediately. Also broaden the regex set to catchString(err)(single-letter variable),err.message, anderror.message, and widen the matched status-code range from 5xx to 4xx-or-5xx so this validation-400 case is in scope.Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/