@contrast/agent 4.23.0 → 4.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -59,6 +59,7 @@ class ExpressFramework {
59
59
  // Express middleware error handler.
60
60
  this.errorHandler = ExpressFramework.ContrastErrorHandler.bind(this);
61
61
  moduleHook.resolve({ name: 'express' }, this.hookExpress.bind(this));
62
+ moduleHook.resolve({ name: 'body-parser' }, this.hookBodyParser.bind(this));
62
63
  }
63
64
 
64
65
  /**
@@ -227,6 +228,80 @@ class ExpressFramework {
227
228
  agentEmitter.on(HTTP_EVENTS.SERVER_LISTEN, this.onServerListen.bind(this));
228
229
  }
229
230
 
231
+ hookBodyParser(bodyParser) {
232
+ const instrumentation = this;
233
+ const origBodyParser = bodyParser;
234
+
235
+ const { json: origJson, raw: origRaw, text: origText, urlencoded: origUrlencoded } = bodyParser;
236
+ const fnArr = [
237
+ {
238
+ key: 'json',
239
+ original: origJson,
240
+ },
241
+ {
242
+ key: 'raw',
243
+ original: origRaw,
244
+ },
245
+ {
246
+ key: 'text',
247
+ original: origText,
248
+ },
249
+ {
250
+ key: 'urlencoded',
251
+ original: origUrlencoded,
252
+ }
253
+ ];
254
+
255
+ bodyParser = function bodyParser(...args) {
256
+ const parser = origBodyParser(...args);
257
+ const hookedParser = function(req, res, next) {
258
+ parser(req, res, instrumentation.contrastNext(req, res, next, 'bodyParser'));
259
+ };
260
+
261
+ Object.defineProperty(hookedParser, 'name', {
262
+ value: 'bodyParser'
263
+ });
264
+
265
+ return hookedParser;
266
+ };
267
+
268
+ fnArr.forEach((fn) => {
269
+ const fnName = `bodyParser.${fn.key}`;
270
+ function contrastHooked(...args) {
271
+ const parser = fn.original(...args);
272
+ const hookedParser = function (req, res, next) {
273
+ parser(req, res, instrumentation.contrastNext(req, res, next, fnName));
274
+ };
275
+
276
+ Object.defineProperty(hookedParser, 'name', {
277
+ value: `${fn.key}Parser`
278
+ });
279
+
280
+ return hookedParser;
281
+ }
282
+
283
+ Object.defineProperty(bodyParser, fn.key, {
284
+ configurable: true,
285
+ enumerable: true,
286
+ get: () => contrastHooked,
287
+ });
288
+ });
289
+
290
+ return bodyParser;
291
+ }
292
+
293
+ contrastNext(req, res, origNext, fnName) {
294
+ return function next() {
295
+ if (fnName == 'bodyParser.json') {
296
+ agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.JSON_VALUE);
297
+ } else {
298
+ agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
299
+ }
300
+
301
+ origNext();
302
+ };
303
+ }
304
+
230
305
  onServerListen(args, server) {
231
306
  if (!this.serversSeen.has(server)) {
232
307
  logger.debug('ignore server.listen: non-express handler');
@@ -316,35 +391,6 @@ class ExpressFramework {
316
391
  next();
317
392
  }, 'multerMiddleware');
318
393
 
319
- // ... body-parser ...............................................
320
- this.useAfter(function ContrastRawBodyParsed(req, res, next) {
321
- agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
322
- next();
323
- }, 'rawParser');
324
-
325
- // ... bodyParser in Sails Framework ............................
326
- this.useAfter(function ContrastBodyParsed(req, res, next) {
327
- if (req._sails && req.body) {
328
- agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
329
- }
330
- next();
331
- }, '_parseHTTPBody');
332
-
333
- this.useAfter(function ContrastTextBodyParsed(req, res, next) {
334
- agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
335
- next();
336
- }, 'textParser');
337
-
338
- this.useAfter(function ContrastBodyParsed(req, res, next) {
339
- agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
340
- next();
341
- }, 'urlencodedParser');
342
-
343
- this.useAfter(function ContrastJSONParsed(req, res, next) {
344
- agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.JSON_VALUE);
345
- next();
346
- }, 'jsonParser');
347
-
348
394
  // ... cookie-parser .............................................
349
395
  this.useAfter(function ContrastCookiesParsed(req, res, next) {
350
396
  agentEmitter.emit(
@@ -16,13 +16,11 @@ Copyright: 2022 Contrast Security, Inc
16
16
 
17
17
  const _ = require('lodash');
18
18
  const semver = require('semver');
19
- const constants = require('../../constants');
20
- const BaseSensor = require('../../hooks/frameworks/base');
21
19
  const patcher = require('../../hooks/patcher');
22
- const { PATCH_TYPES } = require('../../constants');
20
+ const BaseSensor = require('../../hooks/frameworks/base');
21
+ const { PATCH_TYPES, SINK_TYPES } = require('../../constants');
23
22
  const { emitSinkEvent } = require('../../hooks/frameworks/common');
24
23
 
25
- const { SINK_TYPES } = constants;
26
24
  const ID = 'mongodb';
27
25
 
28
26
  function getCursorQueryData(args, version) {
@@ -61,84 +59,98 @@ class MongoDBSensor extends BaseSensor {
61
59
  }
62
60
 
63
61
  install({ ModuleHook }) {
64
- ModuleHook.resolve({ name: 'mongodb' }, (mongodb, { version }) => {
65
- // command(ns, cmd, options, cb)
66
- patcher.patch(mongodb.CoreServer.prototype, 'command', {
67
- alwaysRun: true,
68
- name: 'mongodb.CoreServer.prototype',
69
- patchType: PATCH_TYPES.PROTECT_SINK,
70
- pre: (wrapCtx) => {
71
- emitSinkEvent(
72
- getCursorQueryData(wrapCtx.args, version),
73
- SINK_TYPES.NOSQL_QUERY,
74
- ID
75
- );
76
- }
77
- });
78
-
79
- // cursor(ns, cmd, options)
80
- patcher.patch(mongodb.CoreServer.prototype, 'cursor', {
81
- alwaysRun: true,
82
- name: 'mongodb.CoreServer.prototype',
83
- patchType: PATCH_TYPES.PROTECT_SINK,
84
- pre: (data) => {
85
- emitSinkEvent(
86
- getCursorQueryData(data.args, version),
87
- SINK_TYPES.NOSQL_QUERY,
88
- ID
89
- );
90
- }
62
+ const v4MethodsWithFilter = [
63
+ 'updateOne',
64
+ 'replaceOne',
65
+ 'updateMany',
66
+ 'deleteOne',
67
+ 'deleteMany',
68
+ 'findOneAndDelete',
69
+ 'findOneAndReplace',
70
+ 'findOneAndUpdate',
71
+ 'countDocuments',
72
+ 'count',
73
+ 'distinct',
74
+ ];
75
+
76
+ ModuleHook.resolve({ name: ID, version: '<4.0.0' }, (mongodb, { version }) => {
77
+ ['command', 'cursor'].forEach(method => {
78
+ patcher.patch(mongodb.CoreServer.prototype, method, {
79
+ alwaysRun: true,
80
+ name: `mongodb.CoreServer.prototype.${method}`,
81
+ patchType: PATCH_TYPES.PROTECT_SINK,
82
+ pre: (wrapCtx) => {
83
+ emitSinkEvent(
84
+ getCursorQueryData(wrapCtx.args, version),
85
+ SINK_TYPES.NOSQL_QUERY,
86
+ ID
87
+ );
88
+ }
89
+ });
91
90
  });
92
91
 
93
- // remove(ns, opts, options, cb)
94
- patcher.patch(mongodb.CoreServer.prototype, 'remove', {
95
- alwaysRun: true,
96
- name: 'mongodb.CoreServer.prototype',
97
- patchType: PATCH_TYPES.PROTECT_SINK,
98
- pre: (data) => {
99
- const ops = Array.isArray(data.args[1])
100
- ? data.args[1]
101
- : [data.args[1]];
102
-
103
- for (const op of ops) {
104
- const eData = getOpQueryData(op);
105
- if (eData) {
106
- emitSinkEvent(eData, SINK_TYPES.NOSQL_QUERY, ID);
92
+ ['remove', 'update'].forEach(method => {
93
+ patcher.patch(mongodb.CoreServer.prototype, method, {
94
+ alwaysRun: true,
95
+ name: 'mongodb.CoreServer.prototype.remove',
96
+ patchType: PATCH_TYPES.PROTECT_SINK,
97
+ pre: (wrapCtx) => {
98
+ const ops = Array.isArray(wrapCtx.args[1]) ? wrapCtx.args[1] : [wrapCtx.args[1]];
99
+
100
+ for (const op of ops) {
101
+ const eData = getOpQueryData(op);
102
+ if (eData) {
103
+ emitSinkEvent(eData, SINK_TYPES.NOSQL_QUERY, ID);
104
+ }
107
105
  }
108
106
  }
109
- }
107
+ });
110
108
  });
111
109
 
112
- // update(ns, opts, options, cb)
113
- patcher.patch(mongodb.CoreServer.prototype, 'update', {
110
+ patcher.patch(mongodb.Db.prototype, 'eval', {
114
111
  alwaysRun: true,
115
- name: 'mongodb.CoreServer.prototype',
112
+ name: 'mongodb.Db.prototype.eval',
116
113
  patchType: PATCH_TYPES.PROTECT_SINK,
117
- pre: (data) => {
118
- const ops = Array.isArray(data.args[1])
119
- ? data.args[1]
120
- : [data.args[1]];
121
-
122
- for (const op of ops) {
123
- const eData = getOpQueryData(op);
124
- if (eData) {
125
- emitSinkEvent(eData, SINK_TYPES.NOSQL_QUERY, ID);
126
- }
127
- }
114
+ pre: (wrapCtx) => {
115
+ emitSinkEvent(wrapCtx.args[0], SINK_TYPES.NOSQL_QUERY, ID);
128
116
  }
129
117
  });
118
+ });
130
119
 
131
- // eval(code, parameters, options, cb)
132
- patcher.patch(mongodb.Db.prototype, 'eval', {
120
+ ModuleHook.resolve({ name: ID, version: '>=4.0.0' }, (mongodb) => {
121
+ v4MethodsWithFilter.forEach((method) => {
122
+ patcher.patch(mongodb.Collection.prototype, method, {
123
+ alwaysRun: true,
124
+ name: `mongodb.Collection.prototype.${method}`,
125
+ patchType: PATCH_TYPES.PROTECT_SINK,
126
+ pre: (wrapCtx) => {
127
+ const value = typeof wrapCtx.args[0] == 'function' ? null : wrapCtx.args[0];
128
+ emitSinkEvent(value, SINK_TYPES.NOSQL_QUERY, ID);
129
+ },
130
+ });
131
+ });
132
+
133
+ patcher.patch(mongodb.Db.prototype, 'command', {
133
134
  alwaysRun: true,
134
- name: 'mongodb.Db.prototype',
135
+ name: 'mongodb.Db.prototype.command',
135
136
  patchType: PATCH_TYPES.PROTECT_SINK,
136
- pre: (data) => {
137
- emitSinkEvent(data.args[0], SINK_TYPES.NOSQL_QUERY, ID);
137
+ pre: (wrapCtx) => {
138
+ const value = wrapCtx.args[0] && wrapCtx.args[0].filter;
139
+ emitSinkEvent(value, SINK_TYPES.NOSQL_QUERY, ID);
138
140
  }
139
141
  });
140
142
  });
141
143
 
144
+ ModuleHook.resolve({ name: ID, file: 'lib/cursor/find_cursor', version: '>=4.0.0' }, (cursor) => patcher.patch(cursor, 'FindCursor', {
145
+ alwaysRun: true,
146
+ name: 'mongodb.FindCursor',
147
+ patchType: PATCH_TYPES.PROTECT_SINK,
148
+ pre: (wrapCtx) => {
149
+ const value = wrapCtx.args[2];
150
+ emitSinkEvent(value, SINK_TYPES.NOSQL_QUERY, ID);
151
+ }
152
+ }));
153
+
142
154
  return this;
143
155
  }
144
156
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agent",
3
- "version": "4.23.0",
3
+ "version": "4.23.1",
4
4
  "description": "Node.js security instrumentation by Contrast Security",
5
5
  "keywords": [
6
6
  "security",