From 7970f3bf7d9bdda6493dbf06e1cd5d81202b09be Mon Sep 17 00:00:00 2001 From: lucas Date: Wed, 20 Aug 2025 14:43:02 +0100 Subject: [PATCH 1/4] migrate change to V7 --- CHANGELOG.md | 16 ++++ packages/core/src/js/integrations/sdkinfo.ts | 39 +++++++-- .../core/test/integrations/sdkinfo.test.ts | 84 ++++++++++++++++++- samples/react-native/src/App.tsx | 1 + 4 files changed, 133 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 540362c035..251da4af3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,22 @@ ## Unreleased +### Important Changes + +- **fix(browser): Ensure IP address is only inferred by Relay if `sendDefaultPii` is `true`** ([#???](https://github.com/getsentry/sentry-react-native/pull/??)) + +This release includes a fix for a [behaviour change](https://docs.sentry.io/platforms/javascript/migration/v8-to-v9/#behavior-changes) +that was originally introduced with v9 of the JavaScript SDK: User IP Addresses should only be added to Sentry events automatically, +if `sendDefaultPii` was set to `true`. + +However, the change in v9 required further internal adjustment, which should have been included in v10 of the SDK. +To avoid making a major bump, the fix was patched on the current version and not by bumping to V10. +There is _no API_ breakage involved and hence it is safe to update. +However, after updating the SDK, events (errors, traces, replays, etc.) sent from the browser, will only include +user IP addresses, if you set `sendDefaultPii: true` in your `Sentry.init` options. + +We apologize for any inconvenience caused! + ## Features - Logs now contains more attributes like release, os and device information ([#5032](https://github.com/getsentry/sentry-react-native/pull/5032)) diff --git a/packages/core/src/js/integrations/sdkinfo.ts b/packages/core/src/js/integrations/sdkinfo.ts index 9a5ba222f0..89a5600dee 100644 --- a/packages/core/src/js/integrations/sdkinfo.ts +++ b/packages/core/src/js/integrations/sdkinfo.ts @@ -4,6 +4,14 @@ import { isExpoGo, notWeb } from '../utils/environment'; import { SDK_NAME, SDK_PACKAGE_NAME, SDK_VERSION } from '../version'; import { NATIVE } from '../wrapper'; +// TODO: Remove this on JS V10. +interface IpPatchedSdkInfo extends SdkInfoType { + settings?: { + infer_ip?: 'auto' | 'never'; + }; +} + + const INTEGRATION_NAME = 'SdkInfo'; type DefaultSdkInfo = Pick, 'name' | 'packages' | 'version'>; @@ -18,6 +26,7 @@ export const defaultSdkInfo: DefaultSdkInfo = { ], version: SDK_VERSION, }; +let DefaultPii: boolean | undefined = undefined; /** Default SdkInfo instrumentation */ export const sdkInfoIntegration = (): Integration => { @@ -25,6 +34,17 @@ export const sdkInfoIntegration = (): Integration => { return { name: INTEGRATION_NAME, + setup(client) { + const options = client.getOptions(); + DefaultPii = options.sendDefaultPii; + if (DefaultPii) { + client.on('beforeSendEvent', (event => { + if (event.user?.ip_address === '{{auto}}') { + delete event.user.ip_address; + } + })); + } + }, setupOnce: () => { // noop }, @@ -36,15 +56,24 @@ async function processEvent(event: Event, fetchNativeSdkInfo: () => Promise { expect(processedEvent?.sdk?.name).toEqual(SDK_NAME); expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION); }); + + it('Add none setting when defaultIp is undefined', async () => { + mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null); + const mockEvent: Event = {}; + const processedEvent = await processEvent(mockEvent, {}, undefined); + + expect(processedEvent?.sdk?.name).toEqual(SDK_NAME); + expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION); + // @ts-expect-error injected type. + expect(processedEvent?.sdk?.settings?.infer_ip).toEqual('never'); + }); + + it('Add none setting when defaultIp is false', async () => { + mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null); + const mockEvent: Event = {}; + const processedEvent = await processEvent(mockEvent, {}, false); + + expect(processedEvent?.sdk?.name).toEqual(SDK_NAME); + expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION); + // @ts-expect-error injected type. + expect(processedEvent?.sdk?.settings?.infer_ip).toEqual('never'); + }); + + it('Add auto setting when defaultIp is true', async () => { + mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null); + const mockEvent: Event = {}; + const processedEvent = await processEvent(mockEvent, {}, true); + + expect(processedEvent?.sdk?.name).toEqual(SDK_NAME); + expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION); + // @ts-expect-error injected type. + expect(processedEvent?.sdk?.settings?.infer_ip).toEqual('auto'); + }); + + it('removes ip_address if it is "{{auto}}"', () => { + const mockHandler = jest.fn(); + + const client = { + getOptions: () => ({ sendDefaultPii: true }), + on: (eventName: string, cb: (event: any) => void) => { + if (eventName === 'beforeSendEvent') { + mockHandler.mockImplementation(cb); + } + } + }; + + sdkInfoIntegration().setup!(client as any); + + const testEvent = { user: { ip_address: '{{auto}}' } }; + mockHandler(testEvent); + + expect(testEvent.user.ip_address).toBeUndefined(); + }); + + it('keeps ip_address if it is not "{{auto}}"', () => { + const mockHandler = jest.fn(); + + const client = { + getOptions: () => ({ sendDefaultPii: true }), + on: (eventName: string, cb: (event: any) => void) => { + if (eventName === 'beforeSendEvent') { + mockHandler.mockImplementation(cb); + } + } + }; + + sdkInfoIntegration().setup!(client as any); + + const testEvent = { user: { ip_address: '1.2.3.4' } }; + mockHandler(testEvent); + + expect(testEvent.user.ip_address).toBe('1.2.3.4'); + }); }); -function processEvent(mockedEvent: Event, mockedHint: EventHint = {}): Event | null | PromiseLike { +function processEvent(mockedEvent: Event, mockedHint: EventHint = {}, sendDefaultPii?: boolean): Event | null | PromiseLike { const integration = sdkInfoIntegration(); + if (sendDefaultPii != null) { + const mockClient: jest.Mocked = { + getOptions: jest.fn().mockReturnValue({ sendDefaultPii: sendDefaultPii }), + on: jest.fn(), + } as any; + integration.setup!(mockClient); + } return integration.processEvent!(mockedEvent, mockedHint, {} as any); } diff --git a/samples/react-native/src/App.tsx b/samples/react-native/src/App.tsx index ec67d79385..39e5daee0a 100644 --- a/samples/react-native/src/App.tsx +++ b/samples/react-native/src/App.tsx @@ -66,6 +66,7 @@ Sentry.init({ dsn: getDsn(), debug: true, environment: 'dev', + sendDefaultPii: true, beforeSend: (event: Sentry.ErrorEvent) => { logWithoutTracing('Event beforeSend:', event.event_id); return event; From 777fca6e1f222753b430af9edfb19467685e20ee Mon Sep 17 00:00:00 2001 From: lucas Date: Wed, 20 Aug 2025 15:05:06 +0100 Subject: [PATCH 2/4] clear items --- samples/react-native/src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/react-native/src/App.tsx b/samples/react-native/src/App.tsx index 39e5daee0a..ec67d79385 100644 --- a/samples/react-native/src/App.tsx +++ b/samples/react-native/src/App.tsx @@ -66,7 +66,6 @@ Sentry.init({ dsn: getDsn(), debug: true, environment: 'dev', - sendDefaultPii: true, beforeSend: (event: Sentry.ErrorEvent) => { logWithoutTracing('Event beforeSend:', event.event_id); return event; From 52ad172927d9f80c4c44cbaae7808949390af909 Mon Sep 17 00:00:00 2001 From: LucasZF Date: Wed, 20 Aug 2025 16:55:45 +0100 Subject: [PATCH 3/4] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 251da4af3a..41038d8291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Important Changes -- **fix(browser): Ensure IP address is only inferred by Relay if `sendDefaultPii` is `true`** ([#???](https://github.com/getsentry/sentry-react-native/pull/??)) +- **fix(browser): Ensure IP address is only inferred by Relay if `sendDefaultPii` is `true`** ([#5093](https://github.com/getsentry/sentry-react-native/pull/5093)) This release includes a fix for a [behaviour change](https://docs.sentry.io/platforms/javascript/migration/v8-to-v9/#behavior-changes) that was originally introduced with v9 of the JavaScript SDK: User IP Addresses should only be added to Sentry events automatically, From e20fc027bb82f2cd37f87f1f4699fa4ad4563010 Mon Sep 17 00:00:00 2001 From: lucas Date: Wed, 20 Aug 2025 16:57:43 +0100 Subject: [PATCH 4/4] lint --- packages/core/src/js/integrations/sdkinfo.ts | 7 +++---- packages/core/test/integrations/sdkinfo.test.ts | 10 +++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/core/src/js/integrations/sdkinfo.ts b/packages/core/src/js/integrations/sdkinfo.ts index 89a5600dee..fec8baa7ff 100644 --- a/packages/core/src/js/integrations/sdkinfo.ts +++ b/packages/core/src/js/integrations/sdkinfo.ts @@ -11,7 +11,6 @@ interface IpPatchedSdkInfo extends SdkInfoType { }; } - const INTEGRATION_NAME = 'SdkInfo'; type DefaultSdkInfo = Pick, 'name' | 'packages' | 'version'>; @@ -38,11 +37,11 @@ export const sdkInfoIntegration = (): Integration => { const options = client.getOptions(); DefaultPii = options.sendDefaultPii; if (DefaultPii) { - client.on('beforeSendEvent', (event => { + client.on('beforeSendEvent', event => { if (event.user?.ip_address === '{{auto}}') { delete event.user.ip_address; } - })); + }); } }, setupOnce: () => { @@ -69,7 +68,7 @@ async function processEvent(event: Event, fetchNativeSdkInfo: () => Promise { if (eventName === 'beforeSendEvent') { mockHandler.mockImplementation(cb); } - } + }, }; sdkInfoIntegration().setup!(client as any); @@ -148,7 +148,7 @@ describe('Sdk Info', () => { if (eventName === 'beforeSendEvent') { mockHandler.mockImplementation(cb); } - } + }, }; sdkInfoIntegration().setup!(client as any); @@ -160,7 +160,11 @@ describe('Sdk Info', () => { }); }); -function processEvent(mockedEvent: Event, mockedHint: EventHint = {}, sendDefaultPii?: boolean): Event | null | PromiseLike { +function processEvent( + mockedEvent: Event, + mockedHint: EventHint = {}, + sendDefaultPii?: boolean, +): Event | null | PromiseLike { const integration = sdkInfoIntegration(); if (sendDefaultPii != null) { const mockClient: jest.Mocked = {