diff --git a/packages/datadog-instrumentations/src/express.js b/packages/datadog-instrumentations/src/express.js index c47feef246..b093eab783 100644 --- a/packages/datadog-instrumentations/src/express.js +++ b/packages/datadog-instrumentations/src/express.js @@ -102,11 +102,21 @@ addHook({ }) const processParamsStartCh = channel('datadog:express:process_params:start') -const wrapProcessParamsMethod = (requestPositionInArguments) => { - return (original) => { - return function () { +function wrapProcessParamsMethod (requestPositionInArguments) { + return function wrapProcessParams (original) { + return function wrappedProcessParams () { 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 729f4da033..3081ed9974 100644 --- a/packages/dd-trace/src/appsec/channels.js +++ b/packages/dd-trace/src/appsec/channels.js @@ -17,6 +17,7 @@ module.exports = { setCookieChannel: dc.channel('datadog:iast:set-cookie'), nextBodyParsed: dc.channel('apm:next:body-parsed'), nextQueryParsed: dc.channel('apm:next:query-parsed'), + expressProcessParams: dc.channel('datadog:express:process_params:start'), responseBody: dc.channel('datadog:express:response:json:start'), responseWriteHead: dc.channel('apm:http:server:response:writeHead:start'), httpClientRequestStart: dc.channel('apm:http:client:request:start'), diff --git a/packages/dd-trace/src/appsec/index.js b/packages/dd-trace/src/appsec/index.js index 4fea5ead51..f3656e459e 100644 --- a/packages/dd-trace/src/appsec/index.js +++ b/packages/dd-trace/src/appsec/index.js @@ -12,6 +12,7 @@ const { queryParser, nextBodyParsed, nextQueryParsed, + expressProcessParams, responseBody, responseWriteHead, responseSetHeader @@ -30,6 +31,8 @@ const { storage } = require('../../../datadog-core') const graphql = require('./graphql') const rasp = require('./rasp') +const responseAnalyzedSet = new WeakSet() + let isEnabled = false let config @@ -54,13 +57,14 @@ function enable (_config) { apiSecuritySampler.configure(_config.appsec) + bodyParser.subscribe(onRequestBodyParsed) + cookieParser.subscribe(onRequestCookieParser) incomingHttpRequestStart.subscribe(incomingHttpStartTranslator) incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator) - bodyParser.subscribe(onRequestBodyParsed) + queryParser.subscribe(onRequestQueryParsed) nextBodyParsed.subscribe(onRequestBodyParsed) nextQueryParsed.subscribe(onRequestQueryParsed) - queryParser.subscribe(onRequestQueryParsed) - cookieParser.subscribe(onRequestCookieParser) + expressProcessParams.subscribe(onRequestProcessParams) responseBody.subscribe(onResponseBody) responseWriteHead.subscribe(onResponseWriteHead) responseSetHeader.subscribe(onResponseSetHeader) @@ -79,6 +83,41 @@ function enable (_config) { } } +function onRequestBodyParsed ({ req, res, body, abortController }) { + if (body === undefined || body === null) return + + if (!req) { + const store = storage.getStore() + req = store?.req + } + + const rootSpan = web.root(req) + if (!rootSpan) return + + const results = waf.run({ + persistent: { + [addresses.HTTP_INCOMING_BODY]: body + } + }, req) + + handleResults(results, req, res, rootSpan, abortController) +} + +function onRequestCookieParser ({ req, res, abortController, cookies }) { + if (!cookies || typeof cookies !== 'object') return + + const rootSpan = web.root(req) + if (!rootSpan) return + + const results = waf.run({ + persistent: { + [addresses.HTTP_INCOMING_COOKIES]: cookies + } + }, req) + + handleResults(results, req, res, rootSpan, abortController) +} + function incomingHttpStartTranslator ({ req, res, abortController }) { const rootSpan = web.root(req) if (!rootSpan) return @@ -122,11 +161,6 @@ function incomingHttpEndTranslator ({ req, res }) { persistent[addresses.HTTP_INCOMING_BODY] = req.body } - // TODO: temporary express instrumentation, will use express plugin later - if (req.params !== null && typeof req.params === 'object') { - persistent[addresses.HTTP_INCOMING_PARAMS] = req.params - } - // we need to keep this to support other cookie parsers if (req.cookies !== null && typeof req.cookies === 'object') { persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies @@ -145,24 +179,16 @@ function incomingHttpEndTranslator ({ req, res }) { Reporter.finishRequest(req, res) } -function onRequestBodyParsed ({ req, res, body, abortController }) { - if (body === undefined || body === null) return +function onPassportVerify ({ credentials, user }) { + const store = storage.getStore() + const rootSpan = store?.req && web.root(store.req) - if (!req) { - const store = storage.getStore() - req = store?.req + if (!rootSpan) { + log.warn('No rootSpan found in onPassportVerify') + return } - const rootSpan = web.root(req) - if (!rootSpan) return - - const results = waf.run({ - persistent: { - [addresses.HTTP_INCOMING_BODY]: body - } - }, req) - - handleResults(results, req, res, rootSpan, abortController) + passportTrackEvent(credentials, user, rootSpan, config.appsec.eventTracking.mode) } function onRequestQueryParsed ({ req, res, query, abortController }) { @@ -185,15 +211,15 @@ function onRequestQueryParsed ({ req, res, query, abortController }) { handleResults(results, req, res, rootSpan, abortController) } -function onRequestCookieParser ({ req, res, abortController, cookies }) { - if (!cookies || typeof cookies !== 'object') return - +function onRequestProcessParams ({ 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({ persistent: { - [addresses.HTTP_INCOMING_COOKIES]: cookies + [addresses.HTTP_INCOMING_PARAMS]: params } }, req) @@ -212,20 +238,6 @@ function onResponseBody ({ req, body }) { }, req) } -function onPassportVerify ({ credentials, user }) { - const store = storage.getStore() - const rootSpan = store?.req && web.root(store.req) - - if (!rootSpan) { - log.warn('No rootSpan found in onPassportVerify') - return - } - - passportTrackEvent(credentials, user, rootSpan, config.appsec.eventTracking.mode) -} - -const responseAnalyzedSet = new WeakSet() - function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) { // avoid "write after end" error if (isBlocked(res)) { @@ -287,14 +299,15 @@ function disable () { // Channel#unsubscribe() is undefined for non active channels if (bodyParser.hasSubscribers) bodyParser.unsubscribe(onRequestBodyParsed) + if (cookieParser.hasSubscribers) cookieParser.unsubscribe(onRequestCookieParser) if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator) if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator) + if (passportVerify.hasSubscribers) passportVerify.unsubscribe(onPassportVerify) if (queryParser.hasSubscribers) queryParser.unsubscribe(onRequestQueryParsed) if (nextBodyParsed.hasSubscribers) nextBodyParsed.unsubscribe(onRequestBodyParsed) if (nextQueryParsed.hasSubscribers) nextQueryParsed.unsubscribe(onRequestQueryParsed) - if (cookieParser.hasSubscribers) cookieParser.unsubscribe(onRequestCookieParser) + if (expressProcessParams.hasSubscribers) expressProcessParams.unsubscribe(onRequestProcessParams) if (responseBody.hasSubscribers) responseBody.unsubscribe(onResponseBody) - if (passportVerify.hasSubscribers) passportVerify.unsubscribe(onPassportVerify) if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead) if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader) } diff --git a/packages/dd-trace/test/appsec/express-rules.json b/packages/dd-trace/test/appsec/express-rules.json index 8c5dfeaba3..e8dd910bd0 100644 --- a/packages/dd-trace/test/appsec/express-rules.json +++ b/packages/dd-trace/test/appsec/express-rules.json @@ -28,6 +28,31 @@ ], "transformers": ["lowercase"], "on_match": ["block"] + }, + { + "id": "test-rule-id-2", + "name": "test-rule-name-2", + "tags": { + "type": "security_scanner", + "category": "attack_attempt" + }, + "conditions": [ + { + "parameters": { + "inputs": [ + { + "address": "server.request.path_params" + } + ], + "list": [ + "testattack" + ] + }, + "operator": "phrase_match" + } + ], + "transformers": ["lowercase"], + "on_match": ["block"] } ] } diff --git a/packages/dd-trace/test/appsec/index.express.plugin.spec.js b/packages/dd-trace/test/appsec/index.express.plugin.spec.js index e8b0d4a50e..c38d496623 100644 --- a/packages/dd-trace/test/appsec/index.express.plugin.spec.js +++ b/packages/dd-trace/test/appsec/index.express.plugin.spec.js @@ -1,6 +1,8 @@ 'use strict' -const axios = require('axios') +const Axios = require('axios') +const { assert } = require('chai') +const getPort = require('get-port') const path = require('path') const agent = require('../plugins/agent') const appsec = require('../../src/appsec') @@ -9,8 +11,8 @@ const { json } = require('../../src/appsec/blocked_templates') const zlib = require('zlib') withVersions('express', 'express', version => { - describe('Suspicious request blocking - query', () => { - let port, server, requestBody + describe('Suspicious request blocking - path parameters', () => { + let server, paramCallbackSpy, axios before(() => { return agent.load(['express', 'http'], { client: false }) @@ -18,35 +20,41 @@ withVersions('express', 'express', version => { before((done) => { const express = require('../../../../versions/express').get() - const bodyParser = require('../../../../versions/body-parser').get() const app = express() - app.use(bodyParser.json()) - app.get('/', (req, res) => { - requestBody() - res.end('DONE') + app.get('/multiple-path-params/:parameter1/:parameter2', (req, res) => { + res.send('DONE') }) - app.post('/', (req, res) => { + const nestedRouter = express.Router({ mergeParams: true }) + nestedRouter.get('/:nestedDuplicatedParameter', (req, res) => { res.send('DONE') }) - app.post('/sendjson', (req, res) => { - res.send({ sendResKey: 'sendResValue' }) - }) + app.use('/nested/:nestedDuplicatedParameter', nestedRouter) - app.post('/jsonp', (req, res) => { - res.jsonp({ jsonpResKey: 'jsonpResValue' }) + app.get('/callback-path-param/:callbackedParameter', (req, res) => { + res.send('DONE') }) - app.post('/json', (req, res) => { - res.jsonp({ jsonResKey: 'jsonResValue' }) + const paramCallback = (req, res, next) => { + next() + } + + paramCallbackSpy = sinon.spy(paramCallback) + + app.param(() => { + return paramCallbackSpy }) - server = app.listen(port, () => { - port = server.address().port - done() + app.param('callbackedParameter') + + getPort().then((port) => { + server = app.listen(port, () => { + axios = Axios.create({ baseURL: `http://localhost:${port}` }) + done() + }) }) }) @@ -55,135 +63,330 @@ withVersions('express', 'express', version => { return agent.close({ ritmReset: false }) }) - describe('Blocking', () => { - beforeEach(async () => { - requestBody = sinon.stub() - appsec.enable(new Config({ appsec: { enabled: true, rules: path.join(__dirname, 'express-rules.json') } })) + beforeEach(async () => { + appsec.enable(new Config({ + appsec: { + enabled: true, + rules: path.join(__dirname, 'express-rules.json') + } + })) + }) + + afterEach(() => { + appsec.disable() + sinon.reset() + }) + + describe('route with multiple path parameters', () => { + it('should not block the request when attack is not detected', async () => { + const res = await axios.get('/multiple-path-params/safe_param/safe_param') + + assert.equal(res.status, 200) + assert.equal(res.data, 'DONE') + }) + + it('should block the request when attack is detected in both parameters', async () => { + try { + await axios.get('/multiple-path-params/testattack/testattack') + + return Promise.reject(new Error('Request should not return 200')) + } catch (e) { + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + } + }) + + it('should block the request when attack is detected in the first parameter', async () => { + try { + await axios.get('/multiple-path-params/testattack/safe_param') + + return Promise.reject(new Error('Request should not return 200')) + } catch (e) { + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + } + }) + + it('should block the request when attack is detected in the second parameter', async () => { + try { + await axios.get('/multiple-path-params/safe_param/testattack') + + return Promise.reject(new Error('Request should not return 200')) + } catch (e) { + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + } + }) + }) + + describe('nested routers', () => { + it('should not block the request when attack is not detected', async () => { + const res = await axios.get('/nested/safe_param/safe_param') + + assert.equal(res.status, 200) + assert.equal(res.data, 'DONE') }) - afterEach(() => { - appsec.disable() + it('should block the request when attack is detected in the nested paremeter', async () => { + try { + await axios.get('/nested/safe_param/testattack') + + return Promise.reject(new Error('Request should not return 200')) + } catch (e) { + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + } }) - it('should not block the request without an attack', async () => { - const res = await axios.get(`http://localhost:${port}/?key=value`) + it('should block the request when attack is detected in the parent paremeter', async () => { + try { + await axios.get('/nested/testattack/safe_param') - expect(requestBody).to.be.calledOnce - expect(res.data).to.be.equal('DONE') + return Promise.reject(new Error('Request should not return 200')) + } catch (e) { + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + } + }) + + it('should block the request when attack is detected both parameters', async () => { + try { + await axios.get('/nested/testattack/testattack') + + return Promise.reject(new Error('Request should not return 200')) + } catch (e) { + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + } + }) + }) + + describe('path parameter callback', () => { + it('should not block the request when attack is not detected', async () => { + const res = await axios.get('/callback-path-param/safe_param') + assert.equal(res.status, 200) + assert.equal(res.data, 'DONE') + sinon.assert.calledOnce(paramCallbackSpy) }) it('should block the request when attack is detected', async () => { try { - await axios.get(`http://localhost:${port}/?key=testattack`) + await axios.get('/callback-path-param/testattack') return Promise.reject(new Error('Request should not return 200')) } catch (e) { - expect(e.response.status).to.be.equals(403) - expect(e.response.data).to.be.deep.equal(JSON.parse(json)) - expect(requestBody).not.to.be.called + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + sinon.assert.notCalled(paramCallbackSpy) } }) }) + }) + + describe('Suspicious request blocking - query', () => { + let server, requestBody, axios + + before(() => { + return agent.load(['express', 'http'], { client: false }) + }) - describe('Api Security', () => { - let config + before((done) => { + const express = require('../../../../versions/express').get() - beforeEach(() => { - config = new Config({ - appsec: { - enabled: true, - rules: path.join(__dirname, 'api_security_rules.json'), - apiSecurity: { - enabled: true - } - } + const app = express() + + app.get('/', (req, res) => { + requestBody() + res.end('DONE') + }) + + getPort().then((port) => { + server = app.listen(port, () => { + axios = Axios.create({ baseURL: `http://localhost:${port}` }) + done() }) }) + }) + + after(() => { + server.close() + return agent.close({ ritmReset: false }) + }) + + beforeEach(async () => { + requestBody = sinon.stub() + appsec.enable(new Config({ + appsec: { + enabled: true, + rules: path.join(__dirname, 'express-rules.json') + } + })) + }) + + afterEach(() => { + appsec.disable() + }) + + it('should not block the request without an attack', async () => { + const res = await axios.get('/?key=value') + + assert.equal(res.status, 200) + assert.equal(res.data, 'DONE') + sinon.assert.calledOnce(requestBody) + }) + + it('should block the request when attack is detected', async () => { + try { + await axios.get('/?key=testattack') + + return Promise.reject(new Error('Request should not return 200')) + } catch (e) { + assert.equal(e.response.status, 403) + assert.deepEqual(e.response.data, JSON.parse(json)) + sinon.assert.notCalled(requestBody) + } + }) + }) + + describe('Api Security', () => { + let config, server, axios + + before(() => { + return agent.load(['express', 'http'], { client: false }) + }) + + before((done) => { + const express = require('../../../../versions/express').get() + const bodyParser = require('../../../../versions/body-parser').get() + + const app = express() + app.use(bodyParser.json()) + + app.post('/', (req, res) => { + res.send('DONE') + }) + + app.post('/sendjson', (req, res) => { + res.send({ sendResKey: 'sendResValue' }) + }) - afterEach(() => { - appsec.disable() + app.post('/jsonp', (req, res) => { + res.jsonp({ jsonpResKey: 'jsonpResValue' }) }) - describe('with requestSampling 1.0', () => { - beforeEach(() => { - config.appsec.apiSecurity.requestSampling = 1.0 - appsec.enable(config) + app.post('/json', (req, res) => { + res.jsonp({ jsonResKey: 'jsonResValue' }) + }) + + getPort().then((port) => { + server = app.listen(port, () => { + axios = Axios.create({ baseURL: `http://localhost:${port}` }) + done() }) + }) + }) - function formatSchema (body) { - return zlib.gzipSync(JSON.stringify(body)).toString('base64') + after(() => { + server.close() + return agent.close({ ritmReset: false }) + }) + + beforeEach(() => { + config = new Config({ + appsec: { + enabled: true, + rules: path.join(__dirname, 'api_security_rules.json'), + apiSecurity: { + enabled: true + } } + }) + }) - it('should get the request body schema', async () => { - const expectedRequestBodySchema = formatSchema([{ key: [8] }]) - const res = await axios.post(`http://localhost:${port}/`, { key: 'value' }) + afterEach(() => { + appsec.disable() + }) - await agent.use((traces) => { - const span = traces[0][0] - expect(span.meta).to.haveOwnProperty('_dd.appsec.s.req.body') - expect(span.meta).not.to.haveOwnProperty('_dd.appsec.s.res.body') - expect(span.meta['_dd.appsec.s.req.body']).to.be.equal(expectedRequestBodySchema) - }) + describe('with requestSampling 1.0', () => { + beforeEach(() => { + config.appsec.apiSecurity.requestSampling = 1.0 + appsec.enable(config) + }) - expect(res.status).to.be.equal(200) - expect(res.data).to.be.equal('DONE') - }) + function formatSchema (body) { + return zlib.gzipSync(JSON.stringify(body)).toString('base64') + } - it('should get the response body schema with res.send method with object', async () => { - const expectedResponseBodySchema = formatSchema([{ sendResKey: [8] }]) - const res = await axios.post(`http://localhost:${port}/sendjson`, { key: 'value' }) + it('should get the request body schema', async () => { + const expectedRequestBodySchema = formatSchema([{ key: [8] }]) - await agent.use((traces) => { - const span = traces[0][0] - expect(span.meta['_dd.appsec.s.res.body']).to.be.equal(expectedResponseBodySchema) - }) + const res = await axios.post('/', { key: 'value' }) - expect(res.status).to.be.equal(200) - expect(res.data).to.be.deep.equal({ sendResKey: 'sendResValue' }) + await agent.use((traces) => { + const span = traces[0][0] + assert.property(span.meta, '_dd.appsec.s.req.body') + assert.notProperty(span.meta, '_dd.appsec.s.res.body') + assert.equal(span.meta['_dd.appsec.s.req.body'], expectedRequestBodySchema) }) - it('should get the response body schema with res.json method', async () => { - const expectedResponseBodySchema = formatSchema([{ jsonResKey: [8] }]) - const res = await axios.post(`http://localhost:${port}/json`, { key: 'value' }) + assert.equal(res.status, 200) + assert.equal(res.data, 'DONE') + }) - await agent.use((traces) => { - const span = traces[0][0] - expect(span.meta['_dd.appsec.s.res.body']).to.be.equal(expectedResponseBodySchema) - }) + it('should get the response body schema with res.send method with object', async () => { + const expectedResponseBodySchema = formatSchema([{ sendResKey: [8] }]) + const res = await axios.post('/sendjson', { key: 'value' }) - expect(res.status).to.be.equal(200) - expect(res.data).to.be.deep.equal({ jsonResKey: 'jsonResValue' }) + await agent.use((traces) => { + const span = traces[0][0] + assert.equal(span.meta['_dd.appsec.s.res.body'], expectedResponseBodySchema) }) - it('should get the response body schema with res.jsonp method', async () => { - const expectedResponseBodySchema = formatSchema([{ jsonpResKey: [8] }]) - const res = await axios.post(`http://localhost:${port}/jsonp`, { key: 'value' }) + assert.equal(res.status, 200) + assert.deepEqual(res.data, { sendResKey: 'sendResValue' }) + }) - await agent.use((traces) => { - const span = traces[0][0] - expect(span.meta['_dd.appsec.s.res.body']).to.be.equal(expectedResponseBodySchema) - }) + it('should get the response body schema with res.json method', async () => { + const expectedResponseBodySchema = formatSchema([{ jsonResKey: [8] }]) + const res = await axios.post('/json', { key: 'value' }) - expect(res.status).to.be.equal(200) - expect(res.data).to.be.deep.equal({ jsonpResKey: 'jsonpResValue' }) + await agent.use((traces) => { + const span = traces[0][0] + assert.equal(span.meta['_dd.appsec.s.res.body'], expectedResponseBodySchema) }) - }) - it('should not get the schema', async () => { - config.appsec.apiSecurity.requestSampling = 0 - appsec.enable(config) + assert.equal(res.status, 200) + assert.deepEqual(res.data, { jsonResKey: 'jsonResValue' }) + }) - const res = await axios.post(`http://localhost:${port}/`, { key: 'value' }) + it('should get the response body schema with res.jsonp method', async () => { + const expectedResponseBodySchema = formatSchema([{ jsonpResKey: [8] }]) + const res = await axios.post('/jsonp', { key: 'value' }) await agent.use((traces) => { const span = traces[0][0] - expect(span.meta).not.to.haveOwnProperty('_dd.appsec.s.req.body') - expect(span.meta).not.to.haveOwnProperty('_dd.appsec.s.res.body') + assert.equal(span.meta['_dd.appsec.s.res.body'], expectedResponseBodySchema) }) - expect(res.status).to.be.equal(200) - expect(res.data).to.be.equal('DONE') + assert.equal(res.status, 200) + assert.deepEqual(res.data, { jsonpResKey: 'jsonpResValue' }) + }) + }) + + it('should not get the schema', async () => { + config.appsec.apiSecurity.requestSampling = 0 + appsec.enable(config) + + const res = await axios.post('/', { key: 'value' }) + + await agent.use((traces) => { + const span = traces[0][0] + assert.notProperty(span.meta, '_dd.appsec.s.req.body') + assert.notProperty(span.meta, '_dd.appsec.s.res.body') }) + + assert.equal(res.status, 200) + assert.equal(res.data, 'DONE') }) }) }) diff --git a/packages/dd-trace/test/appsec/index.spec.js b/packages/dd-trace/test/appsec/index.spec.js index bb0b994b0d..4b8c6c0438 100644 --- a/packages/dd-trace/test/appsec/index.spec.js +++ b/packages/dd-trace/test/appsec/index.spec.js @@ -10,10 +10,11 @@ const { cookieParser, incomingHttpRequestStart, incomingHttpRequestEnd, + passportVerify, queryParser, nextBodyParsed, nextQueryParsed, - passportVerify, + expressProcessParams, responseBody, responseWriteHead, responseSetHeader @@ -169,10 +170,11 @@ describe('AppSec Index', function () { it('should subscribe to blockable channels', () => { expect(bodyParser.hasSubscribers).to.be.false expect(cookieParser.hasSubscribers).to.be.false + expect(passportVerify.hasSubscribers).to.be.false expect(queryParser.hasSubscribers).to.be.false expect(nextBodyParsed.hasSubscribers).to.be.false expect(nextQueryParsed.hasSubscribers).to.be.false - expect(passportVerify.hasSubscribers).to.be.false + expect(expressProcessParams.hasSubscribers).to.be.false expect(responseWriteHead.hasSubscribers).to.be.false expect(responseSetHeader.hasSubscribers).to.be.false @@ -180,10 +182,11 @@ describe('AppSec Index', function () { expect(bodyParser.hasSubscribers).to.be.true expect(cookieParser.hasSubscribers).to.be.true + expect(passportVerify.hasSubscribers).to.be.true expect(queryParser.hasSubscribers).to.be.true expect(nextBodyParsed.hasSubscribers).to.be.true expect(nextQueryParsed.hasSubscribers).to.be.true - expect(passportVerify.hasSubscribers).to.be.true + expect(expressProcessParams.hasSubscribers).to.be.true expect(responseWriteHead.hasSubscribers).to.be.true expect(responseSetHeader.hasSubscribers).to.be.true }) @@ -260,10 +263,11 @@ describe('AppSec Index', function () { expect(bodyParser.hasSubscribers).to.be.false expect(cookieParser.hasSubscribers).to.be.false + expect(passportVerify.hasSubscribers).to.be.false expect(queryParser.hasSubscribers).to.be.false expect(nextBodyParsed.hasSubscribers).to.be.false expect(nextQueryParsed.hasSubscribers).to.be.false - expect(passportVerify.hasSubscribers).to.be.false + expect(expressProcessParams.hasSubscribers).to.be.false expect(responseWriteHead.hasSubscribers).to.be.false expect(responseSetHeader.hasSubscribers).to.be.false }) @@ -430,9 +434,6 @@ describe('AppSec Index', function () { route: { path: '/path/:c' }, - params: { - c: '3' - }, cookies: { d: '4', e: '5' @@ -454,7 +455,6 @@ describe('AppSec Index', function () { expect(waf.run).to.have.been.calledOnceWithExactly({ persistent: { 'server.request.body': { a: '1' }, - 'server.request.path_params': { c: '3' }, 'server.request.cookies': { d: '4', e: '5' }, 'server.request.query': { b: '2' } }