fix: filter Safari cashback extension errors (#447)
## Sentry - Issue: PHP-LARAVEL-2K - URL: https://whisper-money.sentry.io/issues/123306290/ ## Root cause Sentry is collecting unhandled errors from a Safari/iOS browser extension content script. Recent production events all came from `webkit-masked-url://hidden/`, function `onResponse`, with message `response.cashbackReminder`, on public app routes. No matching application code exists. ## Fix - Add a narrow client-side Sentry `beforeSend` filter for this Safari cashback extension signature. - Keep similarly named errors from application bundles. - Add Vitest regression coverage. ## Verification - `npx prettier --write resources/js/app.tsx resources/js/lib/sentry.ts resources/js/lib/sentry.test.ts` - `npx eslint --fix resources/js/app.tsx resources/js/lib/sentry.ts resources/js/lib/sentry.test.ts` - `npm test -- resources/js/lib/sentry.test.ts` - `npm run types` (fails on existing unrelated TypeScript errors outside touched files)
This commit is contained in:
parent
edb9f1c852
commit
0b9406714e
|
|
@ -25,6 +25,7 @@ import {
|
|||
isChunkLoadErrorEvent,
|
||||
isFacebookInAppBrowserJavaBridgeNoise,
|
||||
isPostMessageDataCloneNoise,
|
||||
isSafariCashbackExtensionNoise,
|
||||
} from './lib/sentry';
|
||||
import { showSubscriptionPaymentIssueToast } from './lib/subscription-payment-issue-toast';
|
||||
import type { ExpiredBankingConnectionNotification, SharedData } from './types';
|
||||
|
|
@ -42,7 +43,8 @@ Sentry.init({
|
|||
if (
|
||||
isChunkLoadErrorEvent(event) ||
|
||||
isPostMessageDataCloneNoise(event) ||
|
||||
isFacebookInAppBrowserJavaBridgeNoise(event)
|
||||
isFacebookInAppBrowserJavaBridgeNoise(event) ||
|
||||
isSafariCashbackExtensionNoise(event)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
isChunkLoadErrorEvent,
|
||||
isFacebookInAppBrowserJavaBridgeNoise,
|
||||
isPostMessageDataCloneNoise,
|
||||
isSafariCashbackExtensionNoise,
|
||||
} from './sentry';
|
||||
|
||||
describe('isChunkLoadErrorEvent', () => {
|
||||
|
|
@ -87,6 +88,54 @@ describe('isFacebookInAppBrowserJavaBridgeNoise', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('isSafariCashbackExtensionNoise', () => {
|
||||
it('drops Safari cashback extension response handler errors', () => {
|
||||
const event: Event = {
|
||||
exception: {
|
||||
values: [
|
||||
{
|
||||
type: 'TypeError',
|
||||
value: "undefined is not an object (evaluating 'response.cashbackReminder')",
|
||||
stacktrace: {
|
||||
frames: [
|
||||
{
|
||||
filename: 'webkit-masked-url://hidden/',
|
||||
function: 'onResponse',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(isSafariCashbackExtensionNoise(event)).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps cashback errors from application code', () => {
|
||||
const event: Event = {
|
||||
exception: {
|
||||
values: [
|
||||
{
|
||||
type: 'TypeError',
|
||||
value: "undefined is not an object (evaluating 'response.cashbackReminder')",
|
||||
stacktrace: {
|
||||
frames: [
|
||||
{
|
||||
filename: '/build/assets/app.js',
|
||||
function: 'handleResponse',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(isSafariCashbackExtensionNoise(event)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPostMessageDataCloneNoise', () => {
|
||||
it('drops browser postMessage DataCloneError noise', () => {
|
||||
const event: Event = {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const CLONE_ERROR_MESSAGE_PATTERN =
|
|||
/object (can not|could not|couldn't|can't) be cloned/i;
|
||||
const FACEBOOK_IAB_JAVA_OBJECT_GONE_PATTERN =
|
||||
/Error invoking .+: Java object is gone/i;
|
||||
const SAFARI_CASHBACK_EXTENSION_PATTERN = /response\.cashbackReminder/i;
|
||||
|
||||
export function isChunkLoadErrorEvent(event: Event): boolean {
|
||||
return (
|
||||
|
|
@ -55,3 +56,21 @@ export function isFacebookInAppBrowserJavaBridgeNoise(event: Event): boolean {
|
|||
}) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
export function isSafariCashbackExtensionNoise(event: Event): boolean {
|
||||
return (
|
||||
event.exception?.values?.some((exception) => {
|
||||
const exceptionValue = exception.value ?? '';
|
||||
const frames = exception.stacktrace?.frames ?? [];
|
||||
|
||||
return (
|
||||
SAFARI_CASHBACK_EXTENSION_PATTERN.test(exceptionValue) &&
|
||||
frames.some(
|
||||
(frame) =>
|
||||
frame.function === 'onResponse' &&
|
||||
frame.filename === 'webkit-masked-url://hidden/',
|
||||
)
|
||||
);
|
||||
}) ?? false
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue