Skip to content

Non-Error throws produce 200 responses exposing the thrown value #1479

Description

@pi0x

Split out of #1477 (item 1) for separate tracking, since it has the clearest security implications of that batch.

src/response.ts:19 — when a handler's promise rejects, only typeof r === "number" is special-cased into an HTTPError:

(r) => toResponse(typeof r === "number" ? new HTTPError({ status: r }) : r, event, config),

Any other non-Error rejection value re-enters toResponse as if it were a normal handler return value, producing a successful 200 response whose body is the thrown value:

app.get("/boom", () => {
  throw { secret: "db-password", status: 403 };
});
// → HTTP 200, content-type: application/json
// → body: {"secret":"db-password","status":403}

Also verified on current main (75ac47d):

  • throw "not-an-error"200 with body not-an-error
  • throw undefined200 empty response

A caught-and-rethrown non-Error from a database driver or third-party library leaks its full contents to the client with a success status, and onError / unhandled logging never see it. Expected behavior: non-Error rejections should be wrapped into an unhandled 500 HTTPError (e.g. HTTPError.isError(r) ? r : new HTTPError(...) with the value attached as non-public detail), consistent with how thrown Error instances are handled in prepareResponse.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions