Skip to content

Commit 960dd9b

Browse files
authored
fix(v8/node): Ensure express requests are properly handled (#14851)
Backport of #14850 Fixes #14847
1 parent 576a1ad commit 960dd9b

File tree

4 files changed

+123
-5
lines changed

4 files changed

+123
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
2+
const Sentry = require('@sentry/node');
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
transport: loggingTransport,
8+
debug: true,
9+
});
10+
11+
// express must be required after Sentry is initialized
12+
const express = require('express');
13+
const cors = require('cors');
14+
const { startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');
15+
16+
const app = express();
17+
18+
app.use(cors());
19+
20+
app.use((req, _res, next) => {
21+
// We simulate this, which would in other cases be done by some middleware
22+
req.user = {
23+
id: '1',
24+
25+
};
26+
27+
next();
28+
});
29+
30+
app.get('/test1', (_req, _res) => {
31+
throw new Error('error_1');
32+
});
33+
34+
app.use((_req, _res, next) => {
35+
Sentry.setUser({
36+
id: '2',
37+
38+
});
39+
40+
next();
41+
});
42+
43+
app.get('/test2', (_req, _res) => {
44+
throw new Error('error_2');
45+
});
46+
47+
Sentry.setupExpressErrorHandler(app);
48+
49+
startExpressServerAndSendPortToRunner(app);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { cleanupChildProcesses, createRunner } from '../../../utils/runner';
2+
3+
describe('express user handling', () => {
4+
afterAll(() => {
5+
cleanupChildProcesses();
6+
});
7+
8+
test('picks user from request', done => {
9+
createRunner(__dirname, 'server.js')
10+
.expect({
11+
event: {
12+
user: {
13+
id: '1',
14+
15+
},
16+
exception: {
17+
values: [
18+
{
19+
value: 'error_1',
20+
},
21+
],
22+
},
23+
},
24+
})
25+
.start(done)
26+
.makeRequest('get', '/test1', { expectError: true });
27+
});
28+
29+
test('setUser overwrites user from request', done => {
30+
createRunner(__dirname, 'server.js')
31+
.expect({
32+
event: {
33+
user: {
34+
id: '2',
35+
36+
},
37+
exception: {
38+
values: [
39+
{
40+
value: 'error_2',
41+
},
42+
],
43+
},
44+
},
45+
})
46+
.start(done)
47+
.makeRequest('get', '/test2', { expectError: true });
48+
});
49+
});

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ export function addNormalizedRequestDataToEvent(
295295

296296
if (Object.keys(extractedUser).length) {
297297
event.user = {
298-
...event.user,
299298
...extractedUser,
299+
...event.user,
300300
};
301301
}
302302
}

packages/node/src/integrations/tracing/express.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ interface MiddlewareError extends Error {
9393
};
9494
}
9595

96-
type ExpressMiddleware = (
96+
type ExpressMiddleware = (req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void;
97+
98+
type ExpressErrorMiddleware = (
9799
error: MiddlewareError,
98100
req: http.IncomingMessage,
99101
res: http.ServerResponse,
@@ -111,13 +113,17 @@ interface ExpressHandlerOptions {
111113
/**
112114
* An Express-compatible error handler.
113115
*/
114-
export function expressErrorHandler(options?: ExpressHandlerOptions): ExpressMiddleware {
116+
export function expressErrorHandler(options?: ExpressHandlerOptions): ExpressErrorMiddleware {
115117
return function sentryErrorMiddleware(
116118
error: MiddlewareError,
117-
_req: http.IncomingMessage,
119+
request: http.IncomingMessage,
118120
res: http.ServerResponse,
119121
next: (error: MiddlewareError) => void,
120122
): void {
123+
// Ensure we use the express-enhanced request here, instead of the plain HTTP one
124+
// When an error happens, the `expressRequestHandler` middleware does not run, so we set it here too
125+
getIsolationScope().setSDKProcessingMetadata({ request });
126+
121127
const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError;
122128

123129
if (shouldHandleError(error)) {
@@ -152,6 +158,19 @@ export function expressErrorHandler(options?: ExpressHandlerOptions): ExpressMid
152158
};
153159
}
154160

161+
function expressRequestHandler(): ExpressMiddleware {
162+
return function sentryRequestMiddleware(
163+
request: http.IncomingMessage,
164+
_res: http.ServerResponse,
165+
next: () => void,
166+
): void {
167+
// Ensure we use the express-enhanced request here, instead of the plain HTTP one
168+
getIsolationScope().setSDKProcessingMetadata({ request });
169+
170+
next();
171+
};
172+
}
173+
155174
/**
156175
* Add an Express error handler to capture errors to Sentry.
157176
*
@@ -177,9 +196,10 @@ export function expressErrorHandler(options?: ExpressHandlerOptions): ExpressMid
177196
* ```
178197
*/
179198
export function setupExpressErrorHandler(
180-
app: { use: (middleware: ExpressMiddleware) => unknown },
199+
app: { use: (middleware: ExpressMiddleware | ExpressErrorMiddleware) => unknown },
181200
options?: ExpressHandlerOptions,
182201
): void {
202+
app.use(expressRequestHandler());
183203
app.use(expressErrorHandler(options));
184204
ensureIsWrapped(app.use, 'express');
185205
}

0 commit comments

Comments
 (0)