Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e6dcda4

Browse files
crysmagsuuriensimon-id
authored
Removing hook for express v5 router (#6272)
* removing hook for express v5 router & separating v4 router instrumentation to its own `addhook` *Code Origin add router subscription *Appsec update the tests for routerMiddlewareError --------- Co-authored-by: Ugaitz Urien <[email protected]> Co-authored-by: simon-id <[email protected]>
1 parent 694f3c6 commit e6dcda4

File tree

10 files changed

+74
-43
lines changed

10 files changed

+74
-43
lines changed

integration-tests/appsec/standalone-asm.spec.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ describe('Standalone ASM', () => {
7474
assert.strictEqual(payload.length, 1)
7575
assert.isArray(payload[0])
7676

77-
// express.request + 4 middlewares
78-
assert.strictEqual(payload[0].length, 5)
77+
// express.request + router.middleware x 2
78+
assert.strictEqual(payload[0].length, 3)
7979

8080
assertKeep(payload[0][0])
8181
})
@@ -95,7 +95,7 @@ describe('Standalone ASM', () => {
9595
} else {
9696
const fifthReq = payload[0]
9797
assert.isArray(fifthReq)
98-
assert.strictEqual(fifthReq.length, 5)
98+
assert.strictEqual(fifthReq.length, 3)
9999

100100
const { meta, metrics } = fifthReq[0]
101101
assert.notProperty(meta, 'manual.keep')
@@ -288,7 +288,7 @@ describe('Standalone ASM', () => {
288288
} else {
289289
const fifthReq = payload[0]
290290
assert.isArray(fifthReq)
291-
assert.strictEqual(fifthReq.length, 5)
291+
assert.strictEqual(fifthReq.length, 3)
292292
assertKeep(fifthReq[0])
293293
}
294294
}, 40000, 2)
@@ -330,8 +330,8 @@ describe('Standalone ASM', () => {
330330
assert.strictEqual(payload.length, 1)
331331
assert.isArray(payload[0])
332332

333-
// express.request + 4 middlewares
334-
assert.strictEqual(payload[0].length, 5)
333+
// express.request + router.middleware x 2
334+
assert.strictEqual(payload[0].length, 3)
335335

336336
const { meta, metrics } = payload[0][0]
337337
assert.property(meta, '_dd.iast.json') // WEAK_HASH and XCONTENTTYPE_HEADER_MISSING reported
@@ -350,8 +350,8 @@ describe('Standalone ASM', () => {
350350
assert.strictEqual(payload.length, 1)
351351
assert.isArray(payload[0])
352352

353-
// express.request + 4 middlewares
354-
assert.strictEqual(payload[0].length, 5)
353+
// express.request + router.middleware x 2
354+
assert.strictEqual(payload[0].length, 3)
355355

356356
const { meta, metrics } = payload[0][0]
357357
assert.property(meta, '_dd.appsec.json') // crs-942-100 triggered

packages/datadog-instrumentations/src/express.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,16 @@ addHook({ name: 'express', versions: ['>=4'] }, express => {
6767
return express
6868
})
6969

70+
// Express 5 does not rely on router in the same way as v4 and should not be instrumented anymore.
71+
// It would otherwise produce spans for router and express, and so duplicating them.
72+
// We now fall back to router instrumentation
7073
addHook({ name: 'express', versions: ['4'] }, express => {
7174
shimmer.wrap(express.Router, 'use', wrapRouterMethod)
7275
shimmer.wrap(express.Router, 'route', wrapRouterMethod)
7376

7477
return express
7578
})
7679

77-
addHook({ name: 'express', versions: ['>=5.0.0'] }, express => {
78-
shimmer.wrap(express.Router.prototype, 'use', wrapRouterMethod)
79-
shimmer.wrap(express.Router.prototype, 'route', wrapRouterMethod)
80-
81-
return express
82-
})
83-
8480
const queryParserReadCh = channel('datadog:query:read:finish')
8581

8682
function publishQueryParsedAndNext (req, res, next) {

packages/datadog-plugin-express/src/code_origin.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ class ExpressCodeOriginForSpansPlugin extends Plugin {
2222
if (layerTags.has(layer)) return
2323
layerTags.set(layer, entryTags(topOfStackFunc))
2424
})
25+
26+
this.addSub('apm:router:middleware:enter', ({ req, layer }) => {
27+
const tags = layerTags.get(layer)
28+
if (!tags) return
29+
web.getContext(req).span?.addTags(tags)
30+
})
31+
32+
this.addSub('apm:router:route:added', ({ topOfStackFunc, layer }) => {
33+
if (layerTags.has(layer)) return
34+
layerTags.set(layer, entryTags(topOfStackFunc))
35+
})
2536
}
2637
}
2738

packages/datadog-plugin-express/test/code_origin.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('Plugin', () => {
2727
const config = { codeOriginForSpans: { enabled: false } }
2828

2929
describe(`with tracer config ${JSON.stringify(config)}`, () => {
30-
before(() => agent.load(['express', 'http'], [{}, {}, { client: false }], config))
30+
before(() => agent.load(['express', 'http', 'router'], [{}, { client: false }, {}], config))
3131

3232
after(() => agent.close({ ritmReset: false, wipe: true }))
3333

@@ -55,7 +55,7 @@ describe('Plugin', () => {
5555

5656
for (const config of configs) {
5757
describe(`with tracer config ${JSON.stringify(config)}`, () => {
58-
before(() => agent.load(['express', 'http'], [{}, {}, { client: false }], config))
58+
before(() => agent.load(['express', 'http', 'router'], [{}, { client: false }, {}], config))
5959

6060
after(() => agent.close({ ritmReset: false, wipe: true }))
6161

packages/datadog-plugin-express/test/index.spec.js

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('Plugin', () => {
4040

4141
describe('without http', () => {
4242
before(() => {
43-
return agent.load('express', { client: false })
43+
return agent.load(['express', 'router'], [{ client: false }, {}])
4444
})
4545

4646
after(() => {
@@ -94,7 +94,7 @@ describe('Plugin', () => {
9494

9595
describe('without configuration', () => {
9696
before(() => {
97-
return agent.load(['express', 'http'], [{}, { client: false }])
97+
return agent.load(['express', 'http', 'router'], [{}, { client: false }, {}])
9898
})
9999

100100
after(() => {
@@ -230,6 +230,7 @@ describe('Plugin', () => {
230230
const spans = sort(traces[0])
231231
const isExpress4 = semver.intersects(version, '<5.0.0')
232232
let index = 0
233+
const whichMiddleware = isExpress4 ? 'express' : 'router'
233234

234235
const rootSpan = spans[index++]
235236
expect(rootSpan).to.have.property('resource', 'GET /app/user/:id')
@@ -251,31 +252,31 @@ describe('Plugin', () => {
251252
}
252253

253254
expect(spans[index]).to.have.property('resource', 'named')
254-
expect(spans[index]).to.have.property('name', 'express.middleware')
255+
expect(spans[index]).to.have.property('name', `${whichMiddleware}.middleware`)
255256
expect(spans[index].parent_id.toString()).to.equal(rootSpan.span_id.toString())
256-
expect(spans[index].meta).to.have.property('component', 'express')
257+
expect(spans[index].meta).to.have.property('component', whichMiddleware)
257258
index++
258259

259260
expect(spans[index]).to.have.property('resource', 'router')
260-
expect(spans[index]).to.have.property('name', 'express.middleware')
261+
expect(spans[index]).to.have.property('name', `${whichMiddleware}.middleware`)
261262
expect(spans[index].parent_id.toString()).to.equal(rootSpan.span_id.toString())
262-
expect(spans[index].meta).to.have.property('component', 'express')
263+
expect(spans[index].meta).to.have.property('component', whichMiddleware)
263264
index++
264265

265266
if (isExpress4) {
266267
expect(spans[index].resource).to.match(/^bound\s.*$/)
267268
} else {
268269
expect(spans[index]).to.have.property('resource', 'handle')
269270
}
270-
expect(spans[index]).to.have.property('name', 'express.middleware')
271+
expect(spans[index]).to.have.property('name', `${whichMiddleware}.middleware`)
271272
expect(spans[index].parent_id.toString()).to.equal(spans[index - 1].span_id.toString())
272-
expect(spans[index].meta).to.have.property('component', 'express')
273+
expect(spans[index].meta).to.have.property('component', whichMiddleware)
273274
index++
274275

275276
expect(spans[index]).to.have.property('resource', '<anonymous>')
276-
expect(spans[index]).to.have.property('name', 'express.middleware')
277+
expect(spans[index]).to.have.property('name', `${whichMiddleware}.middleware`)
277278
expect(spans[index].parent_id.toString()).to.equal(spans[index - 1].span_id.toString())
278-
expect(spans[index].meta).to.have.property('component', 'express')
279+
expect(spans[index].meta).to.have.property('component', whichMiddleware)
279280

280281
expect(index).to.equal(spans.length - 1)
281282
})
@@ -314,13 +315,16 @@ describe('Plugin', () => {
314315
const spans = sort(traces[0])
315316

316317
const breakingSpanIndex = semver.intersects(version, '<5.0.0') ? 3 : 1
318+
const whichMiddleware = semver.intersects(version, '<5.0.0')
319+
? 'express'
320+
: 'router'
317321

318322
expect(spans[0]).to.have.property('resource', 'GET /user/:id')
319323
expect(spans[0]).to.have.property('name', 'express.request')
320324
expect(spans[0].meta).to.have.property('component', 'express')
321325
expect(spans[breakingSpanIndex]).to.have.property('resource', 'breaking')
322-
expect(spans[breakingSpanIndex]).to.have.property('name', 'express.middleware')
323-
expect(spans[breakingSpanIndex].meta).to.have.property('component', 'express')
326+
expect(spans[breakingSpanIndex]).to.have.property('name', `${whichMiddleware}.middleware`)
327+
expect(spans[breakingSpanIndex].meta).to.have.property('component', whichMiddleware)
324328
})
325329
.then(done)
326330
.catch(done)
@@ -359,12 +363,15 @@ describe('Plugin', () => {
359363
.assertSomeTraces(traces => {
360364
const spans = sort(traces[0])
361365
const errorSpanIndex = semver.intersects(version, '<5.0.0') ? 4 : 2
366+
const whichMiddleware = semver.intersects(version, '<5.0.0')
367+
? 'express'
368+
: 'router'
362369

363370
expect(spans[0]).to.have.property('name', 'express.request')
364-
expect(spans[errorSpanIndex]).to.have.property('name', 'express.middleware')
371+
expect(spans[errorSpanIndex]).to.have.property('name', `${whichMiddleware}.middleware`)
365372
expect(spans[errorSpanIndex].meta).to.have.property(ERROR_TYPE, error.name)
366373
expect(spans[0].meta).to.have.property('component', 'express')
367-
expect(spans[errorSpanIndex].meta).to.have.property('component', 'express')
374+
expect(spans[errorSpanIndex].meta).to.have.property('component', whichMiddleware)
368375
})
369376
.then(done)
370377
.catch(done)
@@ -1176,6 +1183,9 @@ describe('Plugin', () => {
11761183
.assertSomeTraces(traces => {
11771184
const spans = sort(traces[0])
11781185
const secondErrorIndex = spans.length - 2
1186+
const whichMiddleware = semver.intersects(version, '<5.0.0')
1187+
? 'express'
1188+
: 'router'
11791189

11801190
expect(spans[0]).to.have.property('error', 1)
11811191
expect(spans[0].meta).to.have.property(ERROR_TYPE, error.name)
@@ -1186,7 +1196,7 @@ describe('Plugin', () => {
11861196
expect(spans[secondErrorIndex].meta).to.have.property(ERROR_TYPE, error.name)
11871197
expect(spans[secondErrorIndex].meta).to.have.property(ERROR_MESSAGE, error.message)
11881198
expect(spans[secondErrorIndex].meta).to.have.property(ERROR_STACK, error.stack)
1189-
expect(spans[secondErrorIndex].meta).to.have.property('component', 'express')
1199+
expect(spans[secondErrorIndex].meta).to.have.property('component', whichMiddleware)
11901200
})
11911201
.then(done)
11921202
.catch(done)
@@ -1448,12 +1458,12 @@ describe('Plugin', () => {
14481458

14491459
describe('with configuration', () => {
14501460
before(() => {
1451-
return agent.load(['express', 'http'], [{
1461+
return agent.load(['express', 'http', 'router'], [{
14521462
service: 'custom',
14531463
validateStatus: code => code < 400,
14541464
headers: ['User-Agent'],
14551465
blocklist: ['/health']
1456-
}, { client: false }])
1466+
}, { client: false }, {}])
14571467
})
14581468

14591469
after(() => {
@@ -1576,9 +1586,9 @@ describe('Plugin', () => {
15761586

15771587
describe('with configuration for middleware disabled', () => {
15781588
before(() => {
1579-
return agent.load(['express', 'http'], [{
1589+
return agent.load(['express', 'http', 'router'], [{
15801590
middleware: false
1581-
}, { client: false }])
1591+
}, { client: false }, { middleware: false }])
15821592
})
15831593

15841594
after(() => {
@@ -1594,8 +1604,8 @@ describe('Plugin', () => {
15941604

15951605
let span
15961606

1597-
app.use((req, res, next) => {
1598-
span = tracer.scope().active()
1607+
app.use(async (req, res, next) => {
1608+
span = await tracer.scope().active()
15991609
next()
16001610
})
16011611

packages/datadog-plugin-express/test/integration-test/client.spec.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ describe('esm', () => {
3939
describe('with DD_TRACE_MIDDLEWARE_TRACING_ENABLED unset', () => {
4040
it('is instrumented', async () => {
4141
proc = await spawnPluginIntegrationTestProc(sandbox.folder, 'server.mjs', agent.port)
42-
const numberOfSpans = semver.intersects(version, '<5.0.0') ? 4 : 3
42+
const numberOfSpans = semver.intersects(version, '<5.0.0') ? 4 : 2
43+
const whichMiddleware = semver.intersects(version, '<5.0.0')
44+
? 'express'
45+
: 'router'
4346

4447
return curlAndAssertMessage(agent, proc, ({ headers, payload }) => {
4548
assert.propertyVal(headers, 'host', `127.0.0.1:${agent.port}`)
@@ -48,7 +51,7 @@ describe('esm', () => {
4851
assert.isArray(payload[0])
4952
assert.strictEqual(payload[0].length, numberOfSpans)
5053
assert.propertyVal(payload[0][0], 'name', 'express.request')
51-
assert.propertyVal(payload[0][1], 'name', 'express.middleware')
54+
assert.propertyVal(payload[0][1], 'name', `${whichMiddleware}.middleware`)
5255
})
5356
}).timeout(50000)
5457
})

packages/dd-trace/src/appsec/channels.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module.exports = {
3737
responseSetHeader: dc.channel('datadog:http:server:response:set-header:start'),
3838
responseWriteHead: dc.channel('apm:http:server:response:writeHead:start'),
3939
routerParam: dc.channel('datadog:router:param:start'),
40+
routerMiddlewareError: dc.channel('apm:router:middleware:error'),
4041
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
4142
setUncaughtExceptionCaptureCallbackStart: dc.channel('datadog:process:setUncaughtExceptionCaptureCallback:start'),
4243
startGraphqlResolve: dc.channel('datadog:graphql:resolver:start'),

packages/dd-trace/src/appsec/rasp/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ const web = require('../../plugins/util/web')
44
const {
55
setUncaughtExceptionCaptureCallbackStart,
66
expressMiddlewareError,
7-
fastifyMiddlewareError
7+
fastifyMiddlewareError,
8+
routerMiddlewareError
89
} = require('../channels')
910
const { block, registerBlockDelegation, isBlocked } = require('../blocking')
1011
const ssrf = require('./ssrf')
@@ -117,6 +118,7 @@ function enable (config) {
117118

118119
expressMiddlewareError.subscribe(blockOnDatadogRaspAbortError)
119120
fastifyMiddlewareError.subscribe(blockOnDatadogRaspAbortError)
121+
routerMiddlewareError.subscribe(blockOnDatadogRaspAbortError)
120122
}
121123

122124
function disable () {
@@ -129,6 +131,7 @@ function disable () {
129131

130132
expressMiddlewareError.unsubscribe(blockOnDatadogRaspAbortError)
131133
fastifyMiddlewareError.unsubscribe(blockOnDatadogRaspAbortError)
134+
routerMiddlewareError.unsubscribe(blockOnDatadogRaspAbortError)
132135
}
133136

134137
module.exports = {

packages/dd-trace/test/appsec/rasp/command_injection.express.plugin.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ describe('RASP - command_injection', () => {
5757
}
5858

5959
before(() => {
60-
return agent.load(['express', 'http', 'child_process'], { client: false })
60+
return agent.load(['express', 'http', 'child_process', 'router'], { client: false })
6161
})
6262

6363
before((done) => {

packages/dd-trace/test/appsec/rasp/index.spec.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ describe('RASP', () => {
3030
subscribe: sinon.stub(),
3131
unsubscribe: sinon.stub(),
3232
hasSubscribers: true
33-
}
33+
},
34+
routerMiddlewareError: {
35+
subscribe: sinon.stub(),
36+
unsubscribe: sinon.stub(),
37+
hasSubscribers: true
38+
},
3439
}
3540

3641
blocked = false
@@ -72,13 +77,15 @@ describe('RASP', () => {
7277
it('should subscribe to error channels', () => {
7378
sinon.assert.calledOnce(channels.expressMiddlewareError.subscribe)
7479
sinon.assert.calledOnce(channels.fastifyMiddlewareError.subscribe)
80+
sinon.assert.calledOnce(channels.routerMiddlewareError.subscribe)
7581
})
7682

7783
it('should unsubscribe from error channels', () => {
7884
rasp.disable()
7985

8086
sinon.assert.calledOnce(channels.expressMiddlewareError.unsubscribe)
8187
sinon.assert.calledOnce(channels.fastifyMiddlewareError.unsubscribe)
88+
sinon.assert.calledOnce(channels.routerMiddlewareError.unsubscribe)
8289
})
8390
})
8491

0 commit comments

Comments
 (0)