@contrast/agent 4.22.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.
@@ -103,18 +103,25 @@ module.exports = class CallContext {
103
103
  return !!(str && typeof str === 'object' && str[PROXY_TARGET]);
104
104
  }
105
105
 
106
- static getDisplayRange(arg) {
106
+ static getDisplayRange(arg, orgArg = arg, iteration = 0) {
107
107
  if (tracker.getData(arg)) {
108
108
  return new TagRange(0, arg.length - 1, 'untrusted');
109
109
  }
110
110
 
111
111
  if (arg && typeof arg === 'object') {
112
112
  for (const key in arg) {
113
+ if (arg[key] && typeof arg[key] === 'object' && iteration < 5) {
114
+ const nestedDisplayRange = CallContext.getDisplayRange(arg[key], orgArg, iteration += 1);
115
+ if (!_.isEmpty(nestedDisplayRange)) {
116
+ return nestedDisplayRange;
117
+ }
118
+ }
113
119
  const trackedData = tracker.getData(arg[key]);
114
- if (trackedData) {
120
+ if (trackedData && trackedData.tagRanges.length > 0) {
115
121
  const { start, stop } = trackedData.tagRanges[0];
122
+ const offset = Array.isArray(orgArg) ? 2 : 0;
116
123
  const taintedString = arg[key].substring(start, stop + 1);
117
- const taintRangeStart = CallContext.valueString(arg).indexOf(taintedString);
124
+ const taintRangeStart = CallContext.valueString(orgArg).indexOf(taintedString) + offset;
118
125
  if (taintRangeStart === -1) {
119
126
  // If tracked string is not in the abbreviated stringified obj, disable highlighting
120
127
  return new TagRange(0, 0, 'disable-highlighting');
@@ -178,9 +185,9 @@ module.exports = class CallContext {
178
185
  return value.toString();
179
186
  }
180
187
 
181
- const constructorName = _.get(value, 'constructor.name', 'null');
188
+ const type = _.get(value, 'constructor.name', 'null');
182
189
 
183
- if (constructorName === 'Object' && value) {
190
+ if ((type === 'Object' || type === 'Array') && value) {
184
191
  // make string representation uniform with no new lines and consistent spaces
185
192
  let str = util
186
193
  .inspect(value)
@@ -192,7 +199,7 @@ module.exports = class CallContext {
192
199
 
193
200
  return str;
194
201
  }
195
- return constructorName;
202
+ return type;
196
203
  }
197
204
  };
198
205
 
@@ -331,7 +331,9 @@ module.exports.handle = function() {
331
331
  // always exist, even if an empty array, for production code.
332
332
  if (metadata.sourceEvents && !metadata.sourceEvents.length) {
333
333
  const { sourceEvents, braned } = data.metadata;
334
- if (braned) {
334
+ // If it is a source membrane (could be deserialization membrane), then
335
+ // we need to get a source event.
336
+ if (braned && braned.membrane && braned.membrane.makeReqSourceEvent) {
335
337
  sourceEvents.push(
336
338
  braned.membrane.makeReqSourceEvent(data.result.length)
337
339
  );
@@ -37,7 +37,6 @@ const requiredTags = ['untrusted'];
37
37
  const trackSchemaCommands = {
38
38
  'scan': {
39
39
  attributes: [
40
- 'ExpressionAttributeValues',
41
40
  'ExclusiveStartKey',
42
41
  'ScanFilter'
43
42
  ]
@@ -19,36 +19,74 @@ const { PATCH_TYPES } = require('../../constants');
19
19
  const ModuleHook = require('../../hooks/require');
20
20
  const patcher = require('../../hooks/patcher');
21
21
  const logger = require('../logger')('contrast:arch-component');
22
- const semver = require('semver');
23
22
 
23
+ // Architecture component for versions <3.0.0
24
24
  ModuleHook.resolve(
25
25
  {
26
26
  name: 'mongodb',
27
27
  file: 'lib/mongo_client.js',
28
- version: '>=3.3.0'
28
+ version: '<3.0.0'
29
29
  },
30
- (MongoClient, { version }) => {
31
- if (semver.lt(version, '4.0.0')) {
32
- patcher.patch(MongoClient.prototype, 'connect', {
33
- name: 'MongoClient.connect.arch_component',
34
- patchType: PATCH_TYPES.ARCH_COMPONENT,
35
- alwaysRun: true,
36
- post(ctx) {
37
- if (!ctx.result || !ctx.result.then) {
38
- return;
39
- }
40
-
41
- // We should report only when connection is successful
42
- ctx.result.then(function(client) {
30
+ (MongoClient) => {
31
+ patcher.patch(MongoClient, 'connect', {
32
+ name: 'MongoClient.connect.arch_component',
33
+ patchType: PATCH_TYPES.ARCH_COMPONENT,
34
+ alwaysRun: true,
35
+ pre(ctx) {
36
+ // check if typeof callback == 'function'
37
+ // if yes:
38
+ // - MongoClient.connect executes a cb function, which has access to the connection status
39
+ // if not:
40
+ // - MongoClient.connect should return a promise and we can check it's result in another hook, just as before
41
+ const callbackIndex = ctx.args[2] ? 2 : 1;
42
+ if (ctx.args[callbackIndex] instanceof Function || typeof ctx.args[callbackIndex] === 'function') {
43
+ ctx.args[callbackIndex] = patcher.patch(ctx.args[callbackIndex], {
44
+ name: 'MongoClient.connect.callback.arch_component',
45
+ patchType: PATCH_TYPES.ARCH_COMPONENT,
46
+ alwaysRun: true,
47
+ pre(ctx) {
48
+ const [, db] = ctx.args;
49
+ if (db && db.s.topology && db.s.topology.s) {
50
+ try {
51
+ const server = db.s.topology.s.server.s;
52
+ if (server.pool && server.pool.state == 'connected') {
53
+ const connections = server.pool.availableConnections;
54
+ for (const c of connections) {
55
+ agentEmitter.emit('architectureComponent', {
56
+ vendor: 'MongoDB',
57
+ url: `mongodb://${c.host}:${c.port}`,
58
+ remoteHost: '',
59
+ remotePort: c.port
60
+ });
61
+ }
62
+ }
63
+ } catch (err) {
64
+ logger.warn('unable to report MongoDB architecture component, err: %o', err);
65
+ }
66
+ }
67
+ }
68
+ });
69
+ }
70
+ },
71
+ post(ctx) {
72
+ if (!ctx.result || !ctx.result.then) {
73
+ return;
74
+ }
75
+ // it never gets here if callbacks are used, because the result won't be then-able
76
+ ctx.result.then(db => {
77
+ if (db && db.s && db.s.topology && db.s.topology.s) {
43
78
  try {
44
- const { servers = [] } = ctx.obj.s && ctx.obj.s.options;
45
- for (const server of servers) {
46
- agentEmitter.emit('architectureComponent', {
47
- vendor: 'MongoDB',
48
- url: `mongodb://${server.host}:${server.port}`,
49
- remoteHost: '',
50
- remotePort: server.port,
51
- });
79
+ const server = db.s.topology.s.server.s;
80
+ if (server.pool && server.pool.state == 'connected') {
81
+ const connections = server.pool.availableConnections;
82
+ for (const c of connections) {
83
+ agentEmitter.emit('architectureComponent', {
84
+ vendor: 'MongoDB',
85
+ url: `mongodb://${c.host}:${c.port}`,
86
+ remoteHost: '',
87
+ remotePort: c.port
88
+ });
89
+ }
52
90
  }
53
91
  } catch (err) {
54
92
  logger.warn(
@@ -56,60 +94,96 @@ ModuleHook.resolve(
56
94
  err,
57
95
  );
58
96
  }
59
- });
60
- },
61
- });
62
- }
97
+ }
98
+ });
99
+ }
100
+ });
101
+ }
102
+ );
103
+
104
+ // Architecture component for versions >=3.3.0 <4.0.0
105
+ ModuleHook.resolve(
106
+ {
107
+ name: 'mongodb',
108
+ file: 'lib/mongo_client.js',
109
+ version: '>=3.0.0 <4.0.0'
110
+ },
111
+ (MongoClient, { version }) => {
112
+ patcher.patch(MongoClient.prototype, 'connect', {
113
+ name: 'MongoClient.connect.arch_component',
114
+ patchType: PATCH_TYPES.ARCH_COMPONENT,
115
+ alwaysRun: true,
116
+ post(ctx) {
117
+ if (!ctx.result || !ctx.result.then) {
118
+ return;
119
+ }
120
+
121
+ // We should report only when connection is successful
122
+ ctx.result.then(function(client) {
123
+ try {
124
+ const { servers = [] } = ctx.obj.s && ctx.obj.s.options;
125
+ for (const server of servers) {
126
+ agentEmitter.emit('architectureComponent', {
127
+ vendor: 'MongoDB',
128
+ url: `mongodb://${server.host}:${server.port}`,
129
+ remoteHost: '',
130
+ remotePort: server.port,
131
+ });
132
+ }
133
+ } catch (err) {
134
+ logger.warn(
135
+ 'unable to report MongoDB architecture component, err: %o',
136
+ err,
137
+ );
138
+ }
139
+ });
140
+ },
141
+ });
63
142
  },
64
143
  );
65
144
 
66
- /* Architecture component for >= mongodb@4
67
- * It's not limited in the require hook to >=4.0.0 because
68
- * that would result in confusing logs for the customer that
69
- * we don't support older versions (which is not true) */
145
+ // Architecture component for versions >=4.0.0
70
146
  ModuleHook.resolve(
71
147
  {
72
148
  name: 'mongodb',
73
- version: '>=3.3.0'
149
+ version: '>=4.0.0'
74
150
  },
75
151
  (MongoDB, { version }) => {
76
- if (semver.gte(version, '4.0.0')) {
77
- patcher.patch(MongoDB.MongoClient.prototype, 'connect', {
78
- name: 'MongoClient.connect.arch_component',
79
- patchType: PATCH_TYPES.ARCH_COMPONENT,
80
- alwaysRun: true,
81
- post(ctx) {
82
- if (!ctx.result || !ctx.result.then) {
83
- return;
84
- }
152
+ patcher.patch(MongoDB.MongoClient.prototype, 'connect', {
153
+ name: 'MongoClient.connect.arch_component',
154
+ patchType: PATCH_TYPES.ARCH_COMPONENT,
155
+ alwaysRun: true,
156
+ post(ctx) {
157
+ if (!ctx.result || !ctx.result.then) {
158
+ return;
159
+ }
85
160
 
86
- // We should report only when connection is successful
87
- ctx.result.then(function(client) {
88
- if (client && client.topology && client.topology.s) {
89
- try {
90
- const { servers } = client.topology.s;
91
- for (const [, server] of servers) {
92
- if (server.s && server.s.state === 'connected') {
93
- const { srvServiceName } = server.s.options;
94
- const { address } = server.s.description;
95
- agentEmitter.emit('architectureComponent', {
96
- vendor: 'MongoDB',
97
- url: `${srvServiceName}://${address}`,
98
- remoteHost: '',
99
- remotePort: address.split(':').pop()
100
- });
101
- }
161
+ // We should report only when connection is successful
162
+ ctx.result.then(function(client) {
163
+ if (client && client.topology && client.topology.s) {
164
+ try {
165
+ const { servers } = client.topology.s;
166
+ for (const [, server] of servers) {
167
+ if (server.s && server.s.state === 'connected') {
168
+ const { srvServiceName } = server.s.options;
169
+ const { address } = server.s.description;
170
+ agentEmitter.emit('architectureComponent', {
171
+ vendor: 'MongoDB',
172
+ url: `${srvServiceName}://${address}`,
173
+ remoteHost: '',
174
+ remotePort: address.split(':').pop()
175
+ });
102
176
  }
103
- } catch (err) {
104
- logger.warn(
105
- 'unable to report MongoDB architecture component, err: %o',
106
- err,
107
- );
108
177
  }
178
+ } catch (err) {
179
+ logger.warn(
180
+ 'unable to report MongoDB architecture component, err: %o',
181
+ err,
182
+ );
109
183
  }
110
- });
111
- },
112
- });
113
- }
184
+ }
185
+ });
186
+ },
187
+ });
114
188
  },
115
189
  );
@@ -44,7 +44,7 @@ const KEYS = {
44
44
  REQUEST: 'request',
45
45
  REQ: 'req',
46
46
  RES: 'res',
47
- RESPONSE_CONTENT_TYPE: 'res.contentType',
47
+ RESPONSE_CONTENT_TYPE: 'response.contentType',
48
48
  ROUTE: 'route',
49
49
  RULES: 'defend.rules',
50
50
  SAMPLES: 'defend.samples',
@@ -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
  }
@@ -29,15 +29,19 @@ module.exports = function Finding(details = {}) {
29
29
  }
30
30
 
31
31
  const routes = RoutesListWithObservations(details.routes);
32
+ const properties = objToMap(details.properties).map(
33
+ // ensure string values
34
+ ([key, value]) => [key, String(value)]
35
+ );
32
36
 
33
37
  return new dtm.Finding({
34
- 0: String(details.hash || ''), // 1 hash_code
35
- 2: details.ruleId, // 3 rule_id
36
- 5: objToMap(details.properties), // 6 properties
37
- 6: mapToModelArray(TraceEvent, details.events), // 7 events
38
- 7: preflight, // 8 preflight
39
- 8: details.tags, // 9 tags
40
- 9: details.version, // 10 version
38
+ 0: String(details.hash || ''), // 1 hash_code
39
+ 2: details.ruleId, // 3 rule_id
40
+ 5: properties, // 6 properties
41
+ 6: mapToModelArray(TraceEvent, details.events), // 7 events
42
+ 7: preflight, // 8 preflight
43
+ 8: details.tags, // 9 tags
44
+ 9: details.version, // 10 version
41
45
  10: routes // 11 routes
42
46
  });
43
47
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agent",
3
- "version": "4.22.0",
3
+ "version": "4.23.1",
4
4
  "description": "Node.js security instrumentation by Contrast Security",
5
5
  "keywords": [
6
6
  "security",
@@ -163,7 +163,7 @@
163
163
  "mock-fs": "^5.1.2",
164
164
  "mongodb": "file:test/mock/mongodb",
165
165
  "mongodb-npm": "npm:mongodb@^3.6.5",
166
- "mongoose": "^6.1.1",
166
+ "mongoose": "^6.4.6",
167
167
  "mustache": "^3.0.1",
168
168
  "mysql": "file:test/mock/mysql",
169
169
  "mysql2": "file:test/mock/mysql2",