Skip to content
Open
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
6 changes: 4 additions & 2 deletions sentry.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ export function shouldIgnoreError(event: ErrorEvent): boolean {
const exceptionType = event.exception?.values?.[0]?.type || ''
const culprit = (event as any).culprit || ''

const searchText = `${message} ${exceptionValue} ${exceptionType} ${culprit}`.toLowerCase()
// Match each field independently — concatenating them would let a pattern
// match across unrelated fields and suppress a legitimate event.
const searchTexts = [message, exceptionValue, exceptionType, culprit]

// Check all ignore patterns
for (const patterns of Object.values(IGNORED_ERRORS)) {
for (const pattern of patterns) {
if (searchText.includes(pattern.toLowerCase())) {
if (searchTexts.some((text) => text.toLowerCase().includes(pattern.toLowerCase()))) {
return true
}
}
Expand Down
62 changes: 62 additions & 0 deletions src/utils/__tests__/sentry-ignore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { ErrorEvent } from '@sentry/nextjs'

import { shouldIgnoreError } from '../../../sentry.utils'

function eventWithMessage(message: string): ErrorEvent {
return { type: undefined, message } as ErrorEvent
}

function eventWithException(type: string, value: string): ErrorEvent {
return { type: undefined, exception: { values: [{ type, value }] } } as ErrorEvent
}

describe('shouldIgnoreError — per-field matching', () => {
it('matches a pattern contained in event.message', () => {
expect(shouldIgnoreError(eventWithMessage('User rejected the request'))).toBe(true)
})

it('matches a pattern contained in an exception value', () => {
expect(shouldIgnoreError(eventWithException('Error', 'MetaMask: user rejected signature'))).toBe(true)
})

it('matches case-insensitively', () => {
expect(shouldIgnoreError(eventWithMessage('USER REJECTED the request'))).toBe(true)
})

it('does not match across field boundaries (message + exception value concatenation)', () => {
// 'User rejected' split across two fields: the old concatenated-blob
// matching would have suppressed this unrelated event.
const event = {
type: undefined,
message: 'Signing failed for User',
exception: { values: [{ type: 'Error', value: 'rejected promise left unhandled' }] },
} as unknown as ErrorEvent
expect(shouldIgnoreError(event)).toBe(false)
})

it('still reports unrelated errors', () => {
expect(shouldIgnoreError(eventWithMessage('Cannot read properties of undefined'))).toBe(false)
})
})

describe('shouldIgnoreError — existing patterns intact', () => {
it('ignores network noise', () => {
expect(shouldIgnoreError(eventWithException('TypeError', 'Failed to fetch'))).toBe(true)
})

it('ignores extension noise via stack frames', () => {
const event = {
type: undefined,
exception: {
values: [
{
type: 'TypeError',
value: 'boom',
stacktrace: { frames: [{ filename: 'chrome-extension://abc/content.js' }] },
},
],
},
} as unknown as ErrorEvent
expect(shouldIgnoreError(event)).toBe(true)
})
})
Loading