diff --git a/packages/worker/client/sentry-browser-filters.node.test.ts b/packages/worker/client/sentry-browser-filters.node.test.ts index c9bd80fa5f..6f74187a70 100644 --- a/packages/worker/client/sentry-browser-filters.node.test.ts +++ b/packages/worker/client/sentry-browser-filters.node.test.ts @@ -7,6 +7,7 @@ import { filterFathomRemoveChildNullSentryEvent, filterFirefoxDomPermissionDeniedSentryEvent, filterMetaMaskExtensionSentryEvent, + filterOgTypeMetaQuerySelectorContentSentryEvent, filterTwitterInAppBrowserConfigSentryEvent, } from './sentry-browser-filters.ts' @@ -370,4 +371,72 @@ test('browser Sentry filters drop AbortError and Firefox Xray noise and keep rea }, }), ).toBeNull() + + // Injected unguarded og:type meta probe (issue 7660258027 / + // KODY-CLOUDFLARE-46). Requires both the Safari evaluating wording and a + // `global code` frame — same message from app bundles stays. + expect( + filterOgTypeMetaQuerySelectorContentSentryEvent({ + exception: { + values: [ + { + type: 'TypeError', + value: + "null is not an object (evaluating 'document.querySelector(\"meta[property='og:type']\").content')", + stacktrace: { + frames: [ + { + function: 'global code', + filename: '/guides/what-is-kody', + abs_path: 'https://heykody.app/guides/what-is-kody', + }, + ], + }, + }, + ], + }, + }), + ).toBeNull() + expect( + filterOgTypeMetaQuerySelectorContentSentryEvent({ + exception: { + values: [ + { + type: 'TypeError', + value: + "null is not an object (evaluating 'document.querySelector(\"meta[property='og:type']\").content')", + stacktrace: { + frames: [ + { + function: 'applyDocumentHead', + filename: 'https://heykody.app/assets/document-head.js', + }, + ], + }, + }, + ], + }, + }), + ).not.toBeNull() + expect( + filterBrowserSentryEvent({ + exception: { + values: [ + { + type: 'TypeError', + value: + "TypeError: null is not an object (evaluating 'document.querySelector(\"meta[property='og:type']\").content')", + stacktrace: { + frames: [ + { + function: 'global code', + absPath: 'https://heykody.app/guides/what-is-kody', + }, + ], + }, + }, + ], + }, + }), + ).toBeNull() }) diff --git a/packages/worker/client/sentry-browser-filters.ts b/packages/worker/client/sentry-browser-filters.ts index 8ab0ec5b1d..ef3a2dd3db 100644 --- a/packages/worker/client/sentry-browser-filters.ts +++ b/packages/worker/client/sentry-browser-filters.ts @@ -444,6 +444,62 @@ export function filterTwitterInAppBrowserConfigSentryEvent< return event } +/** + * Injected page scripts (Mobile Safari / in-app browsers / content scripts) + * sometimes probe Open Graph tags with an unguarded + * `document.querySelector("meta[property='og:type']").content`. When the page + * has no managed OG tags (guides and many authenticated routes), WebKit throws + * and attributes the frame to `global code` on the document URL — never a Kody + * bundle. Signature from production issue 7660258027 / KODY-CLOUDFLARE-46. + * + * Kody only writes `og:type` in document-head / SSR; it never reads it this + * way. Match is intentionally narrow: Safari "null is not an object + * (evaluating 'document.querySelector(…og:type…).content')" wording AND a + * `global code` stack function. Never blanket-drop bare `.content` TypeErrors + * from app code. + */ +const ogTypeMetaQuerySelectorContentMessage = + /^(?:TypeError:\s*)?null is not an object \(evaluating ['"]document\.querySelector\([^)]*meta\[property=['"]og:type['"]\][^)]*\)\.content['"]\)$/i + +export function isOgTypeMetaQuerySelectorContentMessage(message: string) { + return ogTypeMetaQuerySelectorContentMessage.test(message.trim()) +} + +export function isOgTypeMetaQuerySelectorContentError(error: unknown) { + if (typeof error === 'string') { + return isOgTypeMetaQuerySelectorContentMessage(error) + } + if (typeof error !== 'object' || error === null) return false + if (!('message' in error) || typeof error.message !== 'string') return false + return isOgTypeMetaQuerySelectorContentMessage(error.message) +} + +export function isOgTypeMetaQuerySelectorContentSentryEvent( + event: SentryErrorEventLike, + originalException?: unknown, +) { + const hasOgTypeMessage = + isOgTypeMetaQuerySelectorContentError(originalException) || + sentryEventMessages(event).some( + (message) => + typeof message === 'string' && + isOgTypeMetaQuerySelectorContentMessage(message), + ) + if (!hasOgTypeMessage) return false + return sentryEventStackFrameFunctions(event).some( + (name) => name === 'global code', + ) +} + +export function filterOgTypeMetaQuerySelectorContentSentryEvent< + T extends SentryErrorEventLike, +>(event: T, originalException?: unknown): T | null { + if (isOgTypeMetaQuerySelectorContentSentryEvent(event, originalException)) { + return null + } + return event +} + /** Combined browser beforeSend / capture gate used by the client SDK. */ export function filterBrowserSentryEvent( event: T, @@ -482,5 +538,13 @@ export function filterBrowserSentryEvent( ) { return null } + if ( + filterOgTypeMetaQuerySelectorContentSentryEvent( + event, + originalException, + ) === null + ) { + return null + } return event } diff --git a/packages/worker/client/sentry-init.ts b/packages/worker/client/sentry-init.ts index 0f516feff9..4b4893ad49 100644 --- a/packages/worker/client/sentry-init.ts +++ b/packages/worker/client/sentry-init.ts @@ -32,12 +32,14 @@ export function initBrowserSentry(config: SentryClientConfig) { // permission-denied from Replay/hydration on restricted nodes, // injected wallet/`__firefox__` globals, Fathom beacon // removeChild-on-null, Chrome extension IPC "Object Not Found - // Matching Id…", MetaMask inpage connect failures, and Twitter/X - // in-app browser chrome `CONFIG` ReferenceErrors) — see - // filterBrowserSentryEvent / KODY-CLOUDFLARE-23 / + // Matching Id…", MetaMask inpage connect failures, Twitter/X + // in-app browser chrome `CONFIG` ReferenceErrors, and injected + // unguarded `meta[property='og:type']` probes from `global code`) — + // see filterBrowserSentryEvent / KODY-CLOUDFLARE-23 / // KODY-CLOUDFLARE-3Q / KODY-CLOUDFLARE-3S / KODY-CLOUDFLARE-3X / - // KODY-CLOUDFLARE-43 / issues 7639685398, 7648833360, 7648833403, - // 7653117289, 7655189301, 7658961865, 7659616372. + // KODY-CLOUDFLARE-43 / KODY-CLOUDFLARE-46 / issues 7639685398, + // 7648833360, 7648833403, 7653117289, 7655189301, 7658961865, + // 7659616372, 7660258027. beforeSend(event, hint) { return filterBrowserSentryEvent(event, hint.originalException) },