Skip to content

Commit 1276dfb

Browse files
FIX: missing ip settings from SDK package (#957)
* testing * add infer_ip to sdkinfo processor * changelog * Apply suggestions from code review * remove auto
1 parent 6b39a69 commit 1276dfb

3 files changed

Lines changed: 136 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,25 @@
88
99
## Unreleased
1010

11+
### Important Changes
12+
13+
- **fix(browser): Ensure IP address is only inferred by Relay if `sendDefaultPii` is `true`** ([#957](https://github.com/getsentry/sentry-capacitor/pull/957))
14+
15+
This release includes a fix for a [behaviour change](https://docs.sentry.io/platforms/javascript/migration/v8-to-v9/#behavior-changes)
16+
that was originally introduced with v9 of the JavaScript SDK: User IP Addresses should only be added to Sentry events automatically,
17+
if `sendDefaultPii` was set to `true`.
18+
19+
However, the change in v9 required further internal adjustment, which should have been included in v10 of the SDK.
20+
To avoid making a major bump, the fix was patched on the current version and not by bumping to V10.
21+
There is _no API_ breakage involved and hence it is safe to update.
22+
However, after updating the SDK, events (errors, traces, replays, etc.) sent from the browser, will only include
23+
user IP addresses, if you set `sendDefaultPii: true` in your `Sentry.init` options.
24+
25+
We apologize for any inconvenience caused!
26+
1127
### Features
1228

13-
- Support for Swift Package Manager ([#938](https://github.com/getsentry/sentry-capacitor/pull/938))
29+
- Support for Swift Package Manager ([#938](https://github.com/getsentry/sentry-capacitor/pull/938))
1430

1531
### Dependencies
1632

@@ -97,7 +113,7 @@ Sentry.init({
97113
enableLogs: true,
98114
},
99115
});
100-
````
116+
```
101117

102118
You can also filter the logs being collected by adding beforeSendLogs into `_experiments`
103119

src/integrations/sdkinfo.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
1-
import type { Event, Integration, Package } from '@sentry/core';
1+
import type { Event, Integration, Package, SdkInfo } from '@sentry/core';
22
import { logger } from '@sentry/core';
33
import { SDK_NAME, SDK_VERSION } from '../version';
44
import { NATIVE } from '../wrapper';
55

6+
// TODO: Remove this on JS V10.
7+
interface IpPatchedSdkInfo extends SdkInfo {
8+
settings?: {
9+
infer_ip?: 'auto' | 'never';
10+
};
11+
}
12+
613
const INTEGRATION_NAME = 'SdkInfo';
714

815
let NativeSdkPackage: Package | null = null;
16+
let DefaultPii: boolean | undefined = undefined;
917

1018
export const sdkInfoIntegration = (): Integration => {
1119
return {
1220
name: INTEGRATION_NAME,
1321
processEvent: processEvent,
22+
setup(client) {
23+
const options = client.getOptions();
24+
DefaultPii = options.sendDefaultPii;
25+
if (DefaultPii) {
26+
client.on('beforeSendEvent', (event => {
27+
if (event.user?.ip_address === '{{auto}}') {
28+
delete event.user.ip_address;
29+
}
30+
}));
31+
}
32+
}
1433
};
15-
};
34+
}
1635

1736
async function processEvent(event: Event): Promise<Event> {
1837
// The native SDK info package here is only used on iOS as `beforeSend` is not called on `captureEnvelope`.
@@ -29,18 +48,27 @@ async function processEvent(event: Event): Promise<Event> {
2948
}
3049

3150
event.platform = event.platform || 'javascript';
32-
event.sdk = event.sdk || {};
33-
event.sdk.name = event.sdk.name || SDK_NAME;
34-
event.sdk.version = event.sdk.version || SDK_VERSION;
35-
event.sdk.packages = [
51+
const sdk = (event.sdk || {}) as IpPatchedSdkInfo;
52+
sdk.name = sdk.name || SDK_NAME;
53+
sdk.version = sdk.version || SDK_VERSION;
54+
sdk.packages = [
3655
// default packages are added by baseclient and should not be added here
37-
...(event.sdk.packages || []),
56+
...(sdk.packages || []),
3857
...((NativeSdkPackage && [NativeSdkPackage]) || []),
3958
{
4059
name: 'npm:@sentry/capacitor',
4160
version: SDK_VERSION,
4261
},
4362
];
4463

64+
// Patch missing infer_ip.
65+
sdk.settings = {
66+
infer_ip: DefaultPii ? 'auto' : 'never',
67+
// purposefully allowing already passed settings to override the default
68+
...sdk.settings
69+
};
70+
71+
event.sdk = sdk;
72+
4573
return event;
4674
}

test/integrations/sdkinfo.test.ts

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Event, EventHint, Package } from '@sentry/core';
1+
import type { Client,Event, EventHint, Package } from '@sentry/core';
22
import { SDK_NAME, SDK_VERSION } from '../../src/';
33
import { sdkInfoIntegration } from '../../src/integrations';
44
import { NATIVE } from '../../src/wrapper';
@@ -30,6 +30,7 @@ jest.mock('../../src/wrapper', () => {
3030

3131
describe('Sdk Info', () => {
3232
afterEach(() => {
33+
3334
NATIVE.platform = 'ios';
3435
});
3536

@@ -76,9 +77,89 @@ describe('Sdk Info', () => {
7677
expect(processedEvent?.sdk?.name).toEqual(SDK_NAME);
7778
expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION);
7879
});
80+
it('Add none setting when defaultIp is undefined', async () => {
81+
mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null);
82+
const mockEvent: Event = {};
83+
const processedEvent = await processEvent(mockEvent, {}, undefined);
84+
85+
expect(processedEvent?.sdk?.name).toEqual(SDK_NAME);
86+
expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION);
87+
// @ts-expect-error injected type.
88+
expect(processedEvent?.sdk?.settings?.infer_ip).toEqual('never');
89+
});
90+
91+
it('Add none setting when defaultIp is false', async () => {
92+
mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null);
93+
const mockEvent: Event = {};
94+
const processedEvent = await processEvent(mockEvent, {}, false);
95+
96+
expect(processedEvent?.sdk?.name).toEqual(SDK_NAME);
97+
expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION);
98+
// @ts-expect-error injected type.
99+
expect(processedEvent?.sdk?.settings?.infer_ip).toEqual('never');
100+
});
101+
102+
it('Add auto setting when defaultIp is true', async () => {
103+
mockedFetchNativeSdkInfo = jest.fn().mockResolvedValue(null);
104+
const mockEvent: Event = {};
105+
const processedEvent = await processEvent(mockEvent, {}, true);
106+
107+
expect(processedEvent?.sdk?.name).toEqual(SDK_NAME);
108+
expect(processedEvent?.sdk?.version).toEqual(SDK_VERSION);
109+
// @ts-expect-error injected type.
110+
expect(processedEvent?.sdk?.settings?.infer_ip).toEqual('auto');
111+
});
112+
113+
it('removes ip_address if it is "{{auto}}"', () => {
114+
const mockHandler = jest.fn();
115+
116+
const client = {
117+
getOptions: () => ({ sendDefaultPii: true }),
118+
on: (eventName: string, cb: (event: any) => void) => {
119+
if (eventName === 'beforeSendEvent') {
120+
mockHandler.mockImplementation(cb);
121+
}
122+
}
123+
};
124+
125+
sdkInfoIntegration().setup!(client as any);
126+
127+
const testEvent = { user: { ip_address: '{{auto}}' } };
128+
mockHandler(testEvent);
129+
130+
expect(testEvent.user.ip_address).toBeUndefined();
131+
});
132+
133+
it('keeps ip_address if it is not "{{auto}}"', () => {
134+
const mockHandler = jest.fn();
135+
136+
const client = {
137+
getOptions: () => ({ sendDefaultPii: true }),
138+
on: (eventName: string, cb: (event: any) => void) => {
139+
if (eventName === 'beforeSendEvent') {
140+
mockHandler.mockImplementation(cb);
141+
}
142+
}
143+
};
144+
145+
sdkInfoIntegration().setup!(client as any);
146+
147+
const testEvent = { user: { ip_address: '1.2.3.4' } };
148+
mockHandler(testEvent);
149+
150+
expect(testEvent.user.ip_address).toBe('1.2.3.4');
151+
});
79152
});
80153

81-
function processEvent(mockedEvent: Event, mockedHint: EventHint = {}): Event | null | PromiseLike<Event | null> {
154+
function processEvent(mockedEvent: Event, mockedHint: EventHint = {}, sendDefaultPii?: boolean): Event | null | PromiseLike<Event | null> {
82155
const integration = sdkInfoIntegration();
156+
if (sendDefaultPii != null) {
157+
const mockClient: jest.Mocked<Client> = {
158+
getOptions: jest.fn().mockReturnValue({ sendDefaultPii: sendDefaultPii }),
159+
on: jest.fn(),
160+
} as any;
161+
integration.setup!(mockClient);
162+
163+
}
83164
return integration.processEvent!(mockedEvent, mockedHint, {} as any);
84165
}

0 commit comments

Comments
 (0)