From 133a576fce63bb5b70705e783f39fc4fd89f6238 Mon Sep 17 00:00:00 2001 From: priosshrsth Date: Thu, 23 Apr 2026 12:03:22 +0000 Subject: [PATCH 1/3] fix(OUT-3553): drop browser-extension noise from Sentry A multichainWallet crypto wallet extension was leaking "Origin not allowed" errors into Sentry via console-log forwarding. The throw originates inside the extension's content script (its own origin allowlist check) and has nothing to do with our code. Add two filters in the client Sentry init: - `denyUrls` for chrome-/moz-/safari-extension:// so native exceptions originating in extension-owned code are dropped by stack frame. - `beforeSend` marker check that catches extension content scripts logging via console in our page context (no stack frame to filter on). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/instrumentation-client.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/instrumentation-client.ts b/src/instrumentation-client.ts index a503228a..fa806484 100644 --- a/src/instrumentation-client.ts +++ b/src/instrumentation-client.ts @@ -6,6 +6,33 @@ import * as Sentry from '@sentry/nextjs' const dsn = process.env.NEXT_PUBLIC_SENTRY_DSN +// Markers injected by browser-extension content scripts (crypto wallets, etc.) +// that leak console errors into Sentry because they run in our page context. +// See OUT-3553 — the "Origin not allowed" issue was traced to a multichainWallet +// extension's internal allowlist check, not our code. +const EXTENSION_NOISE_MARKERS = [ + 'multichainWallet', + 'contentscriptFunctionCall', + 'postMessageToContentScript', + 'chrome-extension://', + 'moz-extension://', + 'safari-web-extension://', +] + +const isBrowserExtensionNoise = (event: Sentry.ErrorEvent): boolean => { + try { + const probe = JSON.stringify({ + message: event.message, + exception: event.exception, + extra: event.extra, + breadcrumbs: event.breadcrumbs?.slice(-10), + }) + return EXTENSION_NOISE_MARKERS.some((marker) => probe.includes(marker)) + } catch { + return false + } +} + Sentry.init({ dsn, @@ -28,6 +55,14 @@ Sentry.init({ // Enable sending user PII (Personally Identifiable Information) // https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii sendDefaultPii: true, + + // Drop events whose top stack frame is in a browser extension URL. Catches + // native exceptions thrown from extension-owned code. + denyUrls: [/^chrome-extension:\/\//i, /^moz-extension:\/\//i, /^safari(-web)?-extension:\/\//i], + + // Catches extension content scripts that log via console in our page context + // (no stack frame) — see OUT-3553. + beforeSend: (event) => (isBrowserExtensionNoise(event) ? null : event), }) export const onRouterTransitionStart = Sentry.captureRouterTransitionStart From ac102c3b01bc83c810e4e45979099f33a283f880 Mon Sep 17 00:00:00 2001 From: priosshrsth Date: Thu, 23 Apr 2026 12:08:08 +0000 Subject: [PATCH 2/3] refactor(OUT-3553): drop postMessageToContentScript marker `postMessageToContentScript` is a generic function name that could in theory collide with legit console output. `contentscriptFunctionCall` (the message `type` discriminator) and `multichainWallet` are specific enough to stand on their own. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/instrumentation-client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/instrumentation-client.ts b/src/instrumentation-client.ts index fa806484..23c481fd 100644 --- a/src/instrumentation-client.ts +++ b/src/instrumentation-client.ts @@ -13,7 +13,6 @@ const dsn = process.env.NEXT_PUBLIC_SENTRY_DSN const EXTENSION_NOISE_MARKERS = [ 'multichainWallet', 'contentscriptFunctionCall', - 'postMessageToContentScript', 'chrome-extension://', 'moz-extension://', 'safari-web-extension://', From e2ecc563177d52c4b9f8d068783ffe1c757a4840 Mon Sep 17 00:00:00 2001 From: priosshrsth Date: Thu, 23 Apr 2026 12:14:22 +0000 Subject: [PATCH 3/3] refactor(OUT-3553): narrow extension-noise filter to one marker Drop the secondary markers and the breadcrumb probe; `multichainWallet` in the event payload is specific enough on its own, and `denyUrls` still handles extension-originated native exceptions by stack frame. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/instrumentation-client.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/instrumentation-client.ts b/src/instrumentation-client.ts index 23c481fd..e43139e7 100644 --- a/src/instrumentation-client.ts +++ b/src/instrumentation-client.ts @@ -6,17 +6,10 @@ import * as Sentry from '@sentry/nextjs' const dsn = process.env.NEXT_PUBLIC_SENTRY_DSN -// Markers injected by browser-extension content scripts (crypto wallets, etc.) -// that leak console errors into Sentry because they run in our page context. -// See OUT-3553 — the "Origin not allowed" issue was traced to a multichainWallet -// extension's internal allowlist check, not our code. -const EXTENSION_NOISE_MARKERS = [ - 'multichainWallet', - 'contentscriptFunctionCall', - 'chrome-extension://', - 'moz-extension://', - 'safari-web-extension://', -] +// Marker left behind by multichainWallet-family browser extension content +// scripts that log errors via console in our page context (no stack frame +// for `denyUrls` to filter on). See OUT-3553. +const EXTENSION_NOISE_MARKER = 'multichainWallet' const isBrowserExtensionNoise = (event: Sentry.ErrorEvent): boolean => { try { @@ -24,9 +17,8 @@ const isBrowserExtensionNoise = (event: Sentry.ErrorEvent): boolean => { message: event.message, exception: event.exception, extra: event.extra, - breadcrumbs: event.breadcrumbs?.slice(-10), }) - return EXTENSION_NOISE_MARKERS.some((marker) => probe.includes(marker)) + return probe.includes(EXTENSION_NOISE_MARKER) } catch { return false }