Skip to main content

Production error pages don't leak stack traces

ab-002580 · project-snapshot.data-exposure.error-pages-no-stack-traces
Severity: infoactive

Why it matters

A stack trace returned in the HTTP response body hands an attacker your directory layout, framework version, ORM call signatures, and often the exact SQL that failed — all of which let them craft targeted follow-up payloads instead of probing blind. A PrismaClientKnownRequestError in a 500 response reveals your schema field names; a filesystem ENOENT reveals your server paths; a module-not-found error reveals your dependency versions to anyone looking for a matching CVE. AI coding tools produce this failure mode when a developer asks them to "show me why this route is broken" and they wire error.stack into the JSON response "so the frontend can display it," without gating on NODE_ENV or remembering to remove the debug scaffold before shipping.

Severity rationale

Info because leaked stack traces aid reconnaissance but rarely produce direct compromise on their own — the impact depends on what subsequent vulnerabilities the exposed internals help discover.

Remediation

Return a generic message and log the stack server-side:

catch (e) {
  console.error(e)  // server-side log
  return NextResponse.json({ error: 'Internal error' }, { status: 500 })
}

Deeper remediation guidance and cross-reference coverage for this check lives in the data-protection Pro audit — run that after applying this fix for a more exhaustive pass on the same topic.

Detection

  • ID: project-snapshot.data-exposure.error-pages-no-stack-traces
  • Severity: info
  • What to look for: Check error handlers in API routes and global error boundaries. Look for patterns like return new Response(error.stack), res.json({ error: error.stack }), console.log(error.stack) in handlers without env-gating. Also check Next.js custom error pages (app/error.tsx, pages/_error.tsx) for unconditional stack rendering.
  • Pass criteria: No error handler returns the raw stack trace as part of the response body in production. error.message may be returned; full error.stack may not.
  • Fail criteria: At least one handler returns error.stack in the response body without a NODE_ENV !== 'production' gate.
  • Skip (N/A) when: No API routes or custom error handlers exist.
  • Do NOT pass when: Stack is included "for debugging" with a TODO to remove later — production has no later.
  • Report even on pass: "Scanned N error handlers; 0 leak stack traces in production responses."
  • Detail on fail: "app/api/checkout/route.ts catches errors and returns { error: e.stack } unconditionally".
  • Remediation: Return a generic message and log the stack server-side:
    catch (e) {
      console.error(e)  // server-side log
      return NextResponse.json({ error: 'Internal error' }, { status: 500 })
    }
    

Taxons

History