From 3ca1af98e8f44e9cb62544b26330be0ecf516f52 Mon Sep 17 00:00:00 2001 From: olavurellefsen Date: Mon, 28 Sep 2026 22:09:49 +0100 Subject: [PATCH] fix(notifier): avoid logging websocket error payloads --- src/data-pump/notifier.ts | 6 ++-- test/tests/notifier.test.ts | 56 +++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/data-pump/notifier.ts b/src/data-pump/notifier.ts index 8e892e2..fe8622a 100644 --- a/src/data-pump/notifier.ts +++ b/src/data-pump/notifier.ts @@ -113,8 +113,10 @@ export class FlowcoreNotifier { this.subject = new Subject() this.subject.subscribe({ next: this.onWebSocketEvent.bind(this), - error: (error: Error) => { - this.options.logger?.error("Notification stream error:", { error }) + error: (_error: unknown) => { + // WebSocket implementations may include the request URL in error + // payloads. Do not forward the payload or its message to logs. + this.options.logger?.error("Notification stream error") this.eventResolver?.() }, }) diff --git a/test/tests/notifier.test.ts b/test/tests/notifier.test.ts index d19bc2e..ec3bca3 100644 --- a/test/tests/notifier.test.ts +++ b/test/tests/notifier.test.ts @@ -105,8 +105,8 @@ function createFakeFactory(config: FakeFactoryConfig) { return { factory, handles } } -function createRecordingLogger(): FlowcoreLogger & { calls: Record } { - const calls: Record = { +function createRecordingLogger(): FlowcoreLogger & { calls: Record } { + const calls: Record = { debug: [], info: [], warn: [], @@ -114,10 +114,10 @@ function createRecordingLogger(): FlowcoreLogger & { calls: Record calls.debug.push(message), - info: (message) => calls.info.push(message), - warn: (message) => calls.warn.push(message), - error: (message) => calls.error.push(String(message)), + debug: (...args) => calls.debug.push(args), + info: (...args) => calls.info.push(args), + warn: (...args) => calls.warn.push(args), + error: (...args) => calls.error.push(args), } } @@ -151,6 +151,23 @@ describe("FlowcoreNotifier.waitWebSocket — bug fix (v0.20.1)", () => { factoryStub = undefined }) + it("does not log WebSocket error payloads or their URL-bearing message", async () => { + const logger = createRecordingLogger() + const { factory } = createFakeFactory({ + onCreate: (h) => { + h.pendingPostConnect.push((handle) => { + handle.subject.error(new Error("wss://tenant.example/notifications?credential=fake-secret")) + }) + }, + }) + factoryStub = stubProperty(_internals, "createNotificationClient", factory) + + await createNotifier(20_000, logger).wait() + + assertEquals(logger.calls.error, [["Notification stream error"]]) + assertEquals(JSON.stringify(logger.calls).includes("fake-secret"), false) + }) + it("case 1 — WS error during wait() resolves the promise within 50 ms", async () => { const handles: FakeClientHandle[] = [] const { factory } = createFakeFactory({ @@ -590,10 +607,27 @@ describe("FlowcoreNotifier.waitWebSocket — bug fix (v0.20.1)", () => { const notifier = createNotifier(20_000, logger) await notifier.wait() - assertEquals(logger.calls.debug.includes("WebSocket connection opened."), true) - assertEquals(logger.calls.debug.includes("Connection closed: Code [1000], Reason: Disconnected by user"), true) - assertEquals(logger.calls.info.includes("WebSocket connection opened."), false) - assertEquals(logger.calls.info.includes("Connection closed: Code [1000], Reason: Disconnected by user"), false) - assertEquals(logger.calls.info.includes("Attempting reconnection 1 in 1000 ms..."), true) + assertEquals( + logger.calls.debug.some(([message]) => message === "WebSocket connection opened."), + true, + ) + assertEquals( + logger.calls.debug.some( + ([message]) => message === "Connection closed: Code [1000], Reason: Disconnected by user", + ), + true, + ) + assertEquals( + logger.calls.info.some(([message]) => message === "WebSocket connection opened."), + false, + ) + assertEquals( + logger.calls.info.some(([message]) => message === "Connection closed: Code [1000], Reason: Disconnected by user"), + false, + ) + assertEquals( + logger.calls.info.some(([message]) => message === "Attempting reconnection 1 in 1000 ms..."), + true, + ) }) })