Skip to content

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

@CryptoJones

Description

@CryptoJones

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

  1. 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.
  2. 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/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions