diff --git a/packages/datadog-instrumentations/src/express.js b/packages/datadog-instrumentations/src/express.js index b07c38a42fe..5d719eea342 100644 --- a/packages/datadog-instrumentations/src/express.js +++ b/packages/datadog-instrumentations/src/express.js @@ -60,11 +60,21 @@ addHook({ }) const processParamsStartCh = channel('datadog:express:process_params:start') -const wrapProcessParamsMethod = (requestPositionInArguments) => { +function wrapProcessParamsMethod (requestPositionInArguments) { return (original) => { return function () { if (processParamsStartCh.hasSubscribers) { - processParamsStartCh.publish({ req: arguments[requestPositionInArguments] }) + const req = arguments[requestPositionInArguments] + const abortController = new AbortController() + + processParamsStartCh.publish({ + req, + res: req?.res, + abortController, + params: req?.params + }) + + if (abortController.signal.aborted) return } return original.apply(this, arguments) diff --git a/packages/dd-trace/src/appsec/channels.js b/packages/dd-trace/src/appsec/channels.js index 26d3dbb0355..347f4a19dec 100644 --- a/packages/dd-trace/src/appsec/channels.js +++ b/packages/dd-trace/src/appsec/channels.js @@ -9,6 +9,7 @@ module.exports = { graphqlFinishExecute: dc.channel('apm:graphql:execute:finish'), incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'), incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'), + paramsParser: dc.channel('datadog:express:process_params:start'), passportVerify: dc.channel('datadog:passport:verify:finish'), queryParser: dc.channel('datadog:query:read:finish'), setCookieChannel: dc.channel('datadog:iast:set-cookie'), diff --git a/packages/dd-trace/src/appsec/index.js b/packages/dd-trace/src/appsec/index.js index e68bed40127..11160356e3f 100644 --- a/packages/dd-trace/src/appsec/index.js +++ b/packages/dd-trace/src/appsec/index.js @@ -9,6 +9,7 @@ const { graphqlFinishExecute, incomingHttpRequestStart, incomingHttpRequestEnd, + paramsParser, passportVerify, queryParser, nextBodyParsed, @@ -49,6 +50,7 @@ function enable (_config) { nextQueryParsed.subscribe(onRequestQueryParsed) queryParser.subscribe(onRequestQueryParsed) cookieParser.subscribe(onRequestCookieParser) + paramsParser.subscribe(onRequestParamsParsed) graphqlFinishExecute.subscribe(onGraphqlFinishExecute) if (_config.appsec.eventTracking.enabled) { @@ -182,6 +184,19 @@ function onRequestCookieParser ({ req, res, abortController, cookies }) { handleResults(results, req, res, rootSpan, abortController) } +function onRequestParamsParsed ({ req, res, abortController, params }) { + const rootSpan = web.root(req) + if (!rootSpan) return + + if (!params || typeof params !== 'object' || !Object.keys(params).length) return + + const results = waf.run({ + [addresses.HTTP_INCOMING_PARAMS]: params + }, req) + + handleResults(results, req, res, rootSpan, abortController) +} + function onPassportVerify ({ credentials, user }) { const store = storage.getStore() const rootSpan = store && store.req && web.root(store.req) @@ -233,6 +248,7 @@ function disable () { if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator) if (queryParser.hasSubscribers) queryParser.unsubscribe(onRequestQueryParsed) if (cookieParser.hasSubscribers) cookieParser.unsubscribe(onRequestCookieParser) + if (paramsParser.hasSubscribers) paramsParser.unsubscribe(onRequestParamsParsed) if (passportVerify.hasSubscribers) passportVerify.unsubscribe(onPassportVerify) } diff --git a/packages/dd-trace/test/appsec/index.spec.js b/packages/dd-trace/test/appsec/index.spec.js index 643ad0c3c6d..87f9dea41f1 100644 --- a/packages/dd-trace/test/appsec/index.spec.js +++ b/packages/dd-trace/test/appsec/index.spec.js @@ -10,6 +10,7 @@ const { graphqlFinishExecute, incomingHttpRequestStart, incomingHttpRequestEnd, + paramsParser, queryParser, passportVerify } = require('../../src/appsec/channels') @@ -125,6 +126,7 @@ describe('AppSec Index', () => { it('should subscribe to blockable channels', () => { expect(bodyParser.hasSubscribers).to.be.false expect(cookieParser.hasSubscribers).to.be.false + expect(paramsParser.hasSubscribers).to.be.false expect(queryParser.hasSubscribers).to.be.false expect(passportVerify.hasSubscribers).to.be.false expect(graphqlFinishExecute.hasSubscribers).to.be.false @@ -134,6 +136,7 @@ describe('AppSec Index', () => { expect(bodyParser.hasSubscribers).to.be.true expect(cookieParser.hasSubscribers).to.be.true expect(graphqlFinishExecute.hasSubscribers).to.be.true + expect(paramsParser.hasSubscribers).to.be.true expect(queryParser.hasSubscribers).to.be.true expect(passportVerify.hasSubscribers).to.be.true }) @@ -196,6 +199,7 @@ describe('AppSec Index', () => { expect(bodyParser.hasSubscribers).to.be.false expect(cookieParser.hasSubscribers).to.be.false expect(graphqlFinishExecute.hasSubscribers).to.be.false + expect(paramsParser.hasSubscribers).to.be.false expect(queryParser.hasSubscribers).to.be.false expect(passportVerify.hasSubscribers).to.be.false }) @@ -614,7 +618,7 @@ describe('AppSec Index', () => { }) it('Should not call waf if req is unavailable', () => { - const resolvers = { user: [ { id: '1234' } ] } + const resolvers = { user: [{ id: '1234' }] } sinon.stub(waf, 'run') sinon.stub(storage, 'getStore').returns({}) @@ -626,7 +630,7 @@ describe('AppSec Index', () => { it('Should call waf if resolvers is well formatted', () => { const context = { resolvers: { - user: [ { id: '1234' } ] + user: [{ id: '1234' }] } } const rootSpan = {}