Skip to content

Commit c6a338f

Browse files
authored
feat(core)!: Stop setting user in requestDataIntegration (#14898)
This was an express-specific, rather undocumented behavior, and also conflicted with our privacy-by-default stance. Starting in v9, you'll need to manually call `Sentry.setUser()` e.g. in a middleware to set the user on Sentry events. Docs for this are already pending: getsentry/sentry-docs#12224 Extracted this out of #14806
1 parent e48ffef commit c6a338f

File tree

14 files changed

+17
-119
lines changed

14 files changed

+17
-119
lines changed

dev-packages/node-integration-tests/suites/express/requestUser/test.ts

+7-14
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,21 @@ describe('express user handling', () => {
55
cleanupChildProcesses();
66
});
77

8-
test('picks user from request', done => {
8+
test('ignores user from request', done => {
9+
expect.assertions(2);
10+
911
createRunner(__dirname, 'server.js')
1012
.expect({
11-
event: {
12-
user: {
13-
id: '1',
14-
15-
},
16-
exception: {
17-
values: [
18-
{
19-
value: 'error_1',
20-
},
21-
],
22-
},
13+
event: event => {
14+
expect(event.user).toBeUndefined();
15+
expect(event.exception?.values?.[0]?.value).toBe('error_1');
2316
},
2417
})
2518
.start(done)
2619
.makeRequest('get', '/test1', { expectError: true });
2720
});
2821

29-
test('setUser overwrites user from request', done => {
22+
test('using setUser in middleware works', done => {
3023
createRunner(__dirname, 'server.js')
3124
.expect({
3225
event: {

docs/migration/v8-to-v9.md

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ In v9, an `undefined` value will be treated the same as if the value is not defi
8484

8585
- When `skipOpenTelemetrySetup: true` is configured, `httpIntegration({ spans: false })` will be configured by default. This means that you no longer have to specify this yourself in this scenario. With this change, no spans are emitted once `skipOpenTelemetrySetup: true` is configured, without any further configuration being needed.
8686

87+
- The `requestDataIntegration` will no longer automatically set the user from `request.user`. This is an express-specific, undocumented behavior, and also conflicts with our privacy-by-default strategy. Starting in v9, you'll need to manually call `Sentry.setUser()` e.g. in a middleware to set the user on Sentry events.
88+
8789
### `@sentry/browser`
8890

8991
- The `captureUserFeedback` method has been removed. Use `captureFeedback` instead and update the `comments` field to `message`.
@@ -128,6 +130,8 @@ Sentry.init({
128130
});
129131
```
130132

133+
- The `DEFAULT_USER_INCLUDES` constant has been removed.
134+
131135
### `@sentry/react`
132136

133137
- The `wrapUseRoutes` method has been removed. Use `wrapUseRoutesV6` or `wrapUseRoutesV7` instead depending on what version of react router you are using.

packages/astro/src/index.server.ts

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export {
3131
cron,
3232
dataloaderIntegration,
3333
dedupeIntegration,
34-
DEFAULT_USER_INCLUDES,
3534
defaultStackParser,
3635
endSession,
3736
expressErrorHandler,

packages/aws-serverless/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export {
4242
flush,
4343
close,
4444
getSentryRelease,
45-
DEFAULT_USER_INCLUDES,
4645
createGetModuleFromFilename,
4746
anrIntegration,
4847
disableAnrDetectionForCallback,

packages/bun/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ export {
6262
flush,
6363
close,
6464
getSentryRelease,
65-
DEFAULT_USER_INCLUDES,
6665
createGetModuleFromFilename,
6766
anrIntegration,
6867
disableAnrDetectionForCallback,

packages/core/src/integrations/requestdata.ts

+3-43
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,6 @@ export type RequestDataIntegrationOptions = {
1313
ip?: boolean;
1414
query_string?: boolean;
1515
url?: boolean;
16-
user?:
17-
| boolean
18-
| {
19-
id?: boolean;
20-
username?: boolean;
21-
email?: boolean;
22-
};
2316
};
2417
};
2518

@@ -31,11 +24,6 @@ const DEFAULT_OPTIONS = {
3124
ip: false,
3225
query_string: true,
3326
url: true,
34-
user: {
35-
id: true,
36-
username: true,
37-
email: true,
38-
},
3927
},
4028
transactionNamingScheme: 'methodPath' as const,
4129
};
@@ -49,14 +37,6 @@ const _requestDataIntegration = ((options: RequestDataIntegrationOptions = {}) =
4937
include: {
5038
...DEFAULT_OPTIONS.include,
5139
...options.include,
52-
user:
53-
options.include && typeof options.include.user === 'boolean'
54-
? options.include.user
55-
: {
56-
...DEFAULT_OPTIONS.include.user,
57-
// Unclear why TS still thinks `options.include.user` could be a boolean at this point
58-
...((options.include || {}).user as Record<string, boolean>),
59-
},
6040
},
6141
};
6242

@@ -69,16 +49,12 @@ const _requestDataIntegration = ((options: RequestDataIntegrationOptions = {}) =
6949
// that's happened, it will be easier to add this logic in without worrying about unexpected side effects.)
7050

7151
const { sdkProcessingMetadata = {} } = event;
72-
const { request, normalizedRequest, ipAddress } = sdkProcessingMetadata;
52+
const { normalizedRequest, ipAddress } = sdkProcessingMetadata;
7353

7454
const addRequestDataOptions = convertReqDataIntegrationOptsToAddReqDataOpts(_options);
7555

76-
// If this is set, it takes precedence over the plain request object
7756
if (normalizedRequest) {
78-
// Some other data is not available in standard HTTP requests, but can sometimes be augmented by e.g. Express or Next.js
79-
const user = request ? request.user : undefined;
80-
81-
addNormalizedRequestDataToEvent(event, normalizedRequest, { ipAddress, user }, addRequestDataOptions);
57+
addNormalizedRequestDataToEvent(event, normalizedRequest, { ipAddress }, addRequestDataOptions);
8258
return event;
8359
}
8460

@@ -99,7 +75,7 @@ function convertReqDataIntegrationOptsToAddReqDataOpts(
9975
integrationOptions: Required<RequestDataIntegrationOptions>,
10076
): AddRequestDataToEventOptions {
10177
const {
102-
include: { ip, user, ...requestOptions },
78+
include: { ip, ...requestOptions },
10379
} = integrationOptions;
10480

10581
const requestIncludeKeys: string[] = ['method'];
@@ -109,25 +85,9 @@ function convertReqDataIntegrationOptsToAddReqDataOpts(
10985
}
11086
}
11187

112-
let addReqDataUserOpt;
113-
if (user === undefined) {
114-
addReqDataUserOpt = true;
115-
} else if (typeof user === 'boolean') {
116-
addReqDataUserOpt = user;
117-
} else {
118-
const userIncludeKeys: string[] = [];
119-
for (const [key, value] of Object.entries(user)) {
120-
if (value) {
121-
userIncludeKeys.push(key);
122-
}
123-
}
124-
addReqDataUserOpt = userIncludeKeys;
125-
}
126-
12788
return {
12889
include: {
12990
ip,
130-
user: addReqDataUserOpt,
13191
request: requestIncludeKeys.length !== 0 ? requestIncludeKeys : undefined,
13292
},
13393
};

packages/core/src/utils-hoist/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ export type { PromiseBuffer } from './promisebuffer';
6666

6767
// TODO: Remove requestdata export once equivalent integration is used everywhere
6868
export {
69-
DEFAULT_USER_INCLUDES,
7069
addNormalizedRequestDataToEvent,
7170
winterCGHeadersToDict,
7271
winterCGRequestToRequestData,

packages/core/src/utils-hoist/requestdata.ts

+1-37
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@ import type { Event, PolymorphicRequest, RequestEventData, WebFetchHeaders, WebF
22

33
import { parseCookie } from './cookie';
44
import { DEBUG_BUILD } from './debug-build';
5-
import { isPlainObject } from './is';
65
import { logger } from './logger';
76
import { dropUndefinedKeys } from './object';
87
import { getClientIPAddress, ipHeaderNames } from './vendor/getIpAddress';
98

109
const DEFAULT_INCLUDES = {
1110
ip: false,
1211
request: true,
13-
user: true,
1412
};
1513
const DEFAULT_REQUEST_INCLUDES = ['cookies', 'data', 'headers', 'method', 'query_string', 'url'];
16-
export const DEFAULT_USER_INCLUDES = ['id', 'username', 'email'];
1714

1815
/**
1916
* Options deciding what parts of the request to use when enhancing an event
@@ -23,7 +20,6 @@ export type AddRequestDataToEventOptions = {
2320
include?: {
2421
ip?: boolean;
2522
request?: boolean | Array<(typeof DEFAULT_REQUEST_INCLUDES)[number]>;
26-
user?: boolean | Array<(typeof DEFAULT_USER_INCLUDES)[number]>;
2723
};
2824

2925
/** Injected platform-specific dependencies */
@@ -39,24 +35,6 @@ export type AddRequestDataToEventOptions = {
3935
};
4036
};
4137

42-
function extractUserData(
43-
user: {
44-
[key: string]: unknown;
45-
},
46-
keys: boolean | string[],
47-
): { [key: string]: unknown } {
48-
const extractedUser: { [key: string]: unknown } = {};
49-
const attributes = Array.isArray(keys) ? keys : DEFAULT_USER_INCLUDES;
50-
51-
attributes.forEach(key => {
52-
if (user && key in user) {
53-
extractedUser[key] = user[key];
54-
}
55-
});
56-
57-
return extractedUser;
58-
}
59-
6038
/**
6139
* Add already normalized request data to an event.
6240
* This mutates the passed in event.
@@ -65,7 +43,7 @@ export function addNormalizedRequestDataToEvent(
6543
event: Event,
6644
req: RequestEventData,
6745
// This is non-standard data that is not part of the regular HTTP request
68-
additionalData: { ipAddress?: string; user?: Record<string, unknown> },
46+
additionalData: { ipAddress?: string },
6947
options: AddRequestDataToEventOptions,
7048
): void {
7149
const include = {
@@ -87,20 +65,6 @@ export function addNormalizedRequestDataToEvent(
8765
};
8866
}
8967

90-
if (include.user) {
91-
const extractedUser =
92-
additionalData.user && isPlainObject(additionalData.user)
93-
? extractUserData(additionalData.user, include.user)
94-
: {};
95-
96-
if (Object.keys(extractedUser).length) {
97-
event.user = {
98-
...extractedUser,
99-
...event.user,
100-
};
101-
}
102-
}
103-
10468
if (include.ip) {
10569
const ip = (req.headers && getClientIPAddress(req.headers)) || additionalData.ipAddress;
10670
if (ip) {

packages/core/test/lib/integrations/requestdata.test.ts

+2-15
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ describe('`RequestData` integration', () => {
5959

6060
describe('option conversion', () => {
6161
it('leaves `ip` and `user` at top level of `include`', () => {
62-
const requestDataEventProcessor = initWithRequestDataIntegrationOptions({ include: { ip: false, user: true } });
62+
const requestDataEventProcessor = initWithRequestDataIntegrationOptions({ include: { ip: false } });
6363

6464
void requestDataEventProcessor(event, {});
6565
expect(addNormalizedRequestDataToEventSpy).toHaveBeenCalled();
6666
const passedOptions = addNormalizedRequestDataToEventSpy.mock.calls[0]?.[3];
6767

68-
expect(passedOptions?.include).toEqual(expect.objectContaining({ ip: false, user: true }));
68+
expect(passedOptions?.include).toEqual(expect.objectContaining({ ip: false }));
6969
});
7070

7171
it('moves `true` request keys into `request` include, but omits `false` ones', async () => {
@@ -80,18 +80,5 @@ describe('`RequestData` integration', () => {
8080
expect(passedOptions?.include?.request).toEqual(expect.arrayContaining(['data']));
8181
expect(passedOptions?.include?.request).not.toEqual(expect.arrayContaining(['cookies']));
8282
});
83-
84-
it('moves `true` user keys into `user` include, but omits `false` ones', async () => {
85-
const requestDataEventProcessor = initWithRequestDataIntegrationOptions({
86-
include: { user: { id: true, email: false } },
87-
});
88-
89-
void requestDataEventProcessor(event, {});
90-
91-
const passedOptions = addNormalizedRequestDataToEventSpy.mock.calls[0]?.[3];
92-
93-
expect(passedOptions?.include?.user).toEqual(expect.arrayContaining(['id']));
94-
expect(passedOptions?.include?.user).not.toEqual(expect.arrayContaining(['email']));
95-
});
9683
});
9784
});

packages/google-cloud-serverless/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export {
4242
flush,
4343
close,
4444
getSentryRelease,
45-
DEFAULT_USER_INCLUDES,
4645
createGetModuleFromFilename,
4746
anrIntegration,
4847
disableAnrDetectionForCallback,

packages/node/src/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ export { cron } from './cron';
5454

5555
export type { NodeOptions } from './types';
5656

57-
export { DEFAULT_USER_INCLUDES } from '@sentry/core';
58-
5957
export {
6058
// This needs exporting so the NodeClient can be used without calling init
6159
setOpenTelemetryContextAsyncContextStrategy as setNodeAsyncContextStrategy,

packages/remix/src/index.server.ts

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ export {
3434
createTransport,
3535
cron,
3636
dedupeIntegration,
37-
DEFAULT_USER_INCLUDES,
3837
defaultStackParser,
3938
endSession,
4039
expressErrorHandler,

packages/solidstart/src/server/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export {
2626
createTransport,
2727
cron,
2828
dedupeIntegration,
29-
DEFAULT_USER_INCLUDES,
3029
defaultStackParser,
3130
endSession,
3231
expressErrorHandler,

packages/sveltekit/src/server/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export {
2626
createTransport,
2727
cron,
2828
dedupeIntegration,
29-
DEFAULT_USER_INCLUDES,
3029
defaultStackParser,
3130
endSession,
3231
expressErrorHandler,

0 commit comments

Comments
 (0)