Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/setup/telemetry/setupSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
36 changes: 36 additions & 0 deletions tests/unit/setupSentryTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
Loading