Skip to content

Commit 858f056

Browse files
committed
fix(webapp): redact exception details before reporting errors
1 parent 1c8dfbf commit 858f056

3 files changed

Lines changed: 28 additions & 4 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/core": patch
3+
---
4+
5+
Redact common credential and sensitive-data fields from structured logger output by default, including nested values and error metadata. Long strings and arrays are now truncated to keep log entries manageable.

apps/webapp/app/services/logger.server.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Logger.onError = (message, ...args) => {
2121
const extra = redact(flattenArgs(args), SENTRY_EXTRA_FILTERED_KEYS) as Record<string, unknown>;
2222

2323
if (error) {
24-
captureException(error, {
24+
captureException(redactError(error), {
2525
extra: {
2626
message,
2727
...extra,
@@ -35,6 +35,17 @@ Logger.onError = (message, ...args) => {
3535
}
3636
};
3737

38+
function redactError(error: Error): Error {
39+
const redactedError = new Error(redact(error.message) as string);
40+
redactedError.name = error.name;
41+
42+
if (error.stack) {
43+
redactedError.stack = redact(error.stack) as string;
44+
}
45+
46+
return redactedError;
47+
}
48+
3849
function extractErrorFromArgs(args: Array<Record<string, unknown> | undefined>) {
3950
for (const arg of args) {
4051
if (arg && "error" in arg && arg.error instanceof Error) {

apps/webapp/test/logger.server.onError.test.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,25 @@ describe("logger.server Logger.onError", () => {
3030
expect(JSON.stringify(options.extra)).not.toContain("tr_prod_should_not_leak");
3131
});
3232

33-
it("redacts the extra payload sent to Sentry on the captureException path", async () => {
33+
it("redacts the exception and extra payload sent to Sentry", async () => {
3434
const { logger } = await import("~/services/logger.server");
35+
const error = new Error("tr_prod_should_not_leak");
36+
error.stack = "Bearer secret-token";
3537

3638
logger.error("boom", {
37-
error: new Error("bad thing happened"),
39+
error,
3840
payload: { secret: "do-not-leak" },
3941
});
4042

4143
expect(captureExceptionMock).toHaveBeenCalledTimes(1);
42-
const [, options] = captureExceptionMock.mock.calls[0] as [Error, { extra: unknown }];
44+
const [capturedError, options] = captureExceptionMock.mock.calls[0] as [
45+
Error,
46+
{ extra: unknown },
47+
];
4348

49+
expect(capturedError).not.toBe(error);
50+
expect(capturedError.message).not.toContain("tr_prod_should_not_leak");
51+
expect(capturedError.stack).not.toContain("secret-token");
4452
expect(JSON.stringify(options.extra)).not.toContain("do-not-leak");
4553
});
4654

0 commit comments

Comments
 (0)