HarperFast / HarperFast/harper

Custom resource `get()` / constructor error messages leak in HTTP 500 response body

Open
#1,421 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
89
Forks
10
Avg merge
2d 6h
Merged PRs (30d)
200

Description

## Summary

When a custom resource's `get()` method or `constructor()` throws, the error's `.message` string appears verbatim in the JSON HTTP 500 response body's `title` field.

## Symptom

```js
class Secrets extends tables.Secrets {
get() {
throw new Error('DB_URL=postgres://user:s3cr3t@internal-db.corp:5432/prod')
}
}
```

Response body (HTTP 500):
```json
{
"type": "error:Error",
"code": "Error",
"title": "DB_URL=postgres://user:s3cr3t@internal-db.corp:5432/prod"
}
```

The full `.message` is returned to the caller. The `code` and `type` fields also expose the error class name.

## Additional behaviors observed

- Sync throw and rejected Promise are serialized identically (both leak the message).
- `constructor()` throw leaks the same way; the server survives and serves subsequent requests normally.
- Non-Error throws (`throw 'some string'`) surface the string value in `title`.
- `throw new Response(...)` is not recognized as a short-circuit idiom — produces `title: "[object Response]"` (no leak, but also not useful).
- No unhandled-promise-rejections observed — all paths are caught; the leak is only in the serialization.

## Impact

In production, application-layer errors can expose internal implementation details through the API: database connection strings, internal hostnames, secret values embedded in error messages, library internals. The HTTP 500 status code is correct; the information leakage is in the body.

## Recommendation

Scrub `error.message` from the response body in non-development environments; substitute a generic message (e.g. `"Internal server error"`) and log the full error internally. Consider an opt-in `ClientError` subclass whose `.message` is intentionally user-visible, so developers can explicitly choose when to expose error text to callers.

## Environment

Harper: `7aaa5a152` (byte-identical to `main` @ `6797f091d`, reproduces on main).
Repro: `npm run test:integration -- "integrationTests/qa-scratch/qa169-custom-resource-errors.test.ts"` (cells S2, S3, S4)

---
*Surfaced by QA-explorer (QA-169). Filed by Claude (Sonnet 4.6) for @kriszyp.*

Contributor guide

Open the contributing guide

Research direction

Run npm run test:integration -- "integrationTests/qa-scratch/qa169-custom-resource-errors.test.ts" and inspect cells S2, S3, and S4. Trace how custom-resource get() and constructor() failures are serialized for sync throws, rejected Promises, and non-Error values. Done means production HTTP 500 responses no longer expose error messages or class details while the existing status behavior remains covered by tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js, typescript
Domain
api, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
46/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.