diff --git a/src/setup/telemetry/setupSentry.ts b/src/setup/telemetry/setupSentry.ts index 30704aeeb5eb..a977b2863980 100644 --- a/src/setup/telemetry/setupSentry.ts +++ b/src/setup/telemetry/setupSentry.ts @@ -71,6 +71,14 @@ function setupSentry(): void { // `add()` instead of `put()` and fails whenever the key is already there. Onyx never calls `add()`, so a // real Onyx write cannot produce this, and the DOMException carries no frames to tag as third-party. /^ConstraintError: Key already exists in the object store/, + // Calls into the WKWebView message bridge an in-app browser injected into the page, which then tore the + // bridge down (https://github.com/Expensify/App/issues/100268, Sentry APP-8WS). We ship no + // `webkit.messageHandlers` call anywhere in the org, so any error naming one was thrown by injected code + // we cannot act on. Filtering on the message rather than the frames is what works here: WebKit withholds + // the URL of an injected script, so `denyUrls` and `thirdPartyErrorFilterIntegration` have nothing to + // match. The trailing dot keeps this to a real property access into the bridge, so an unrelated error + // that merely names the bridge in prose still reports. + /webkit\.messageHandlers\./, ], denyUrls: EXTENSION_DENY_URLS, beforeSendTransaction: processBeforeSendTransactions, diff --git a/tests/unit/setupSentryTest.ts b/tests/unit/setupSentryTest.ts index 216c402181b8..76063d0fb3fc 100644 --- a/tests/unit/setupSentryTest.ts +++ b/tests/unit/setupSentryTest.ts @@ -70,4 +70,40 @@ describe('setupSentry', () => { // Then it still reports expect(isIgnored(message)).toBe(false); }); + + it('drops the WKWebView bridge call an in-app browser injects into the page', () => { + // Given the message WebKit builds for the APP-8WS rejection, naming the expression whose base was undefined + const message = "TypeError: undefined is not an object (evaluating 'top.webkit.messageHandlers.foregroundToBackground.postMessage')"; + + // When the registered patterns are matched against it + // Then it is ignored, because no code we ship calls `webkit.messageHandlers` + expect(isIgnored(message)).toBe(true); + }); + + it('drops a call into any other injected handler on that same bridge', () => { + // Given the same signature with a different handler name, which is the same injected-bridge noise + const message = "TypeError: undefined is not an object (evaluating 'window.webkit.messageHandlers.contextMenuMessageHandler.postMessage')"; + + // When the registered patterns are matched against it + // Then it is ignored too, rather than needing one pattern per handler name + expect(isIgnored(message)).toBe(true); + }); + + it('keeps an error that only names the bridge in prose, which is not a call into it', () => { + // Given an error that mentions the bridge without accessing a handler on it + const message = 'Error: Failed to set up webkit.messageHandlers'; + + // When the registered patterns are matched against it + // Then it still reports, because the trailing dot limits the pattern to a real property access + expect(isIgnored(message)).toBe(false); + }); + + it('keeps the TypeErrors our own code produces, which read the same way in Safari', () => { + // Given the same WebKit wording for a property access in our bundle + const message = "TypeError: undefined is not an object (evaluating 'policy.employeeList.length')"; + + // When the registered patterns are matched against it + // Then it still reports + expect(isIgnored(message)).toBe(false); + }); });