Skip to content

API: internal error details (stack trace, DB errors) leaked to clients on 500 #1169

Description

@adityachoudhari26

Summary

The API's global error handler returns the raw error message and full stack trace to the client on unhandled (generic 500) errors. This leaks internal implementation details to any caller, including unauthenticated ones. The API is publicly exposed and actively scanned by bots, so this is reachable in practice.

Location

apps/api/src/middleware/error-handler.ts — the generic fallback branch:

// Handle generic errors
return res
  .status(500)
  .json({ message: "Internal server error: " + error.message, code: "INTERNAL_ERROR", details: error.stack });

error.message and error.stack are sent in the response body.

Evidence

An unauthenticated POST /api/argo/<non-uuid>/webhook triggers an unhandled Postgres error and returns:

{
  "message": "Internal server error: invalid input syntax for type uuid: \"...\"",
  "code": "INTERNAL_ERROR",
  "details": "error: invalid input syntax for type uuid: \"...\"\n    at /app/node_modules/pg-pool/index.js:45:11\n    at ...\n    at async file:///app/node_modules/drizzle-orm/node-postgres/session.js:83:22\n    at async handleWebhookRequest (file:///app/apps/api/dist/src/routes/argoworkflow/index.js:20:19)"
}

This exposes internal file paths, dependency layout (drizzle/pg-pool), handler locations, and raw SQL error text.

Impact

Information disclosure (CWE-209: error message containing sensitive information). Leaked stack traces and DB error strings help an attacker map the system, dependencies, and schema. Only the generic-500 fallback is affected; the ApiError and validation branches are fine.

Proposed fix

Return a generic body on 500 and keep the detail server-side only (the handler already logs the full error via logger.error):

return res.status(500).json({ message: "Internal server error", code: "INTERNAL_ERROR" });

Optionally include a correlation/request id in the response to aid log lookup without leaking internals.

Notes

  • Found while wiring up API observability/monitoring.
  • Low effort, contained change to a single middleware.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions