@contrast/agent 4.19.6 → 4.20.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.
@@ -215,13 +215,13 @@
215
215
  },
216
216
  "mongodb.Db.prototype.eval": {
217
217
  "moduleName": "mongodb",
218
- "version": ">=3.5.0",
218
+ "version": ">=3.3.0",
219
219
  "methodName": "Db.prototype.eval",
220
220
  "isModule": true
221
221
  },
222
222
  "mongodb.Collection.prototype.rename": {
223
223
  "moduleName": "mongodb",
224
- "version": ">=3.5.0",
224
+ "version": ">=3.3.0",
225
225
  "methodName": "Collection.prototype.rename",
226
226
  "isModule": true
227
227
  },
@@ -48,10 +48,11 @@ module.exports = ({ common }) => {
48
48
  const mongoSink = {};
49
49
 
50
50
  /**
51
- * Methods to hook in Collection.prototype.
51
+ * Methods to hook in Collection.prototype
52
52
  * Collated and pruned from: Object.keys(mongodb.Collection.prototype)
53
+ * Some methods are deprecated in newer DATABASE versions
53
54
  */
54
- mongoSink.collectionMethodsV3 = [
55
+ mongoSink.collectionMethods = [
55
56
  // XXX hooking these results in compilation issues; unsure why
56
57
  // 'collectionName',
57
58
  // 'namespace',
@@ -91,45 +92,36 @@ module.exports = ({ common }) => {
91
92
  // 'initializeOrderedBulkOp'
92
93
 
93
94
  // 'bulkWrite',
94
- // 'insert',
95
95
 
96
- 'deleteMany',
97
- 'deleteOne',
98
96
  'find',
99
- 'findAndModify',
100
- 'findAndRemove',
101
97
  'findOne',
98
+ 'findAndModify',
102
99
  'findOneAndDelete',
103
100
  'findOneAndReplace',
104
101
  'findOneAndUpdate',
102
+ 'insert',
105
103
  'insertMany',
106
104
  'insertOne',
107
105
  'remove',
108
- 'removeMany',
109
106
  'removeOne',
110
107
  'replaceOne',
108
+ 'removeMany',
111
109
  'save',
112
110
  'update',
111
+ 'updateOne',
113
112
  'updateMany',
114
- 'updateOne'
113
+ 'deleteOne',
114
+ 'deleteMany',
115
115
  ];
116
116
 
117
- mongoSink.collectionMethodsV4 = [
118
- 'deleteMany',
119
- 'deleteOne',
120
- 'find',
121
- 'findOne',
122
- 'findOneAndDelete',
117
+ // Collection of methods with the syntax: method(filter, doc, options);
118
+ // We should track and report unsafe inputs in both: `filter` and `doc`
119
+ const twoArgsCollectionMethods = [
123
120
  'findOneAndReplace',
124
121
  'findOneAndUpdate',
125
- 'insert',
126
- 'insertMany',
127
- 'insertOne',
128
- 'remove',
129
122
  'replaceOne',
130
- 'update',
131
- 'updateMany',
132
- 'updateOne'
123
+ 'updateOne',
124
+ 'updateMany'
133
125
  ];
134
126
 
135
127
  /**
@@ -144,6 +136,7 @@ module.exports = ({ common }) => {
144
136
  ...data,
145
137
  result: null
146
138
  });
139
+
147
140
  // events are prefixed with 'protect '
148
141
  ctxt.signature = data.name.split(' ')[1];
149
142
  Object.defineProperty(doc, '__contrastContext', {
@@ -169,8 +162,10 @@ module.exports = ({ common }) => {
169
162
  };
170
163
 
171
164
  mongoSink.collectionMethodPost = (data) => {
165
+ const method = data.hooked && data.hooked.name;
166
+
172
167
  try {
173
- const doc = data.args[0];
168
+ let doc = data.args[0];
174
169
 
175
170
  if (!doc) {
176
171
  return;
@@ -183,7 +178,14 @@ module.exports = ({ common }) => {
183
178
  }
184
179
 
185
180
  const ctxt = getCallContext(contrastContext, data);
181
+
182
+ if (twoArgsCollectionMethods.indexOf(method) > -1) {
183
+ // Assess both args at once
184
+ doc = { filter: data.args[0], update: data.args[1] };
185
+ }
186
+
186
187
  mongoSink.assess(doc, ctxt);
188
+
187
189
  } catch (err) {
188
190
  logger.error(`Unable to perform post hook in ${data.name} %o`, err);
189
191
  }
@@ -287,13 +289,6 @@ module.exports = ({ common }) => {
287
289
  };
288
290
 
289
291
  mongoSink.handleVersion4 = (mongodb, version) => {
290
- patcher.patch(mongodb.Collection.prototype, mongoSink.collectionMethodsV4, {
291
- name: 'assess mongodb.Collection.prototype',
292
- patchType: PATCH_TYPES.ASSESS_SINK,
293
- pre: mongoSink.collectionMethodHandler,
294
- post: mongoSink.collectionMethodPost
295
- });
296
-
297
292
  patcher.patch(mongodb.Db.prototype, 'command', {
298
293
  name: 'assess mongodb.Db.prototype',
299
294
  patchType: PATCH_TYPES.ASSESS_SINK,
@@ -302,13 +297,6 @@ module.exports = ({ common }) => {
302
297
  };
303
298
 
304
299
  mongoSink.handleVersion3 = (mongodb, version) => {
305
- patcher.patch(mongodb.Collection.prototype, mongoSink.collectionMethodsV3, {
306
- name: 'assess mongodb.Collection.prototype',
307
- patchType: PATCH_TYPES.ASSESS_SINK,
308
- pre: mongoSink.collectionMethodHandler,
309
- post: mongoSink.collectionMethodPost
310
- });
311
-
312
300
  patcher.patch(
313
301
  mongodb.CoreServer.prototype,
314
302
  mongoSink.coreServerQueryMethods,
@@ -342,7 +330,15 @@ module.exports = ({ common }) => {
342
330
  return;
343
331
  }
344
332
 
333
+ // NPM package architectures are different between v3.x.x and v4.x.x
345
334
  requireHook.resolve({ name: 'mongodb' }, function(mongodb, { version }) {
335
+ patcher.patch(mongodb.Collection.prototype, mongoSink.collectionMethods, {
336
+ name: 'assess mongodb.Collection.prototype',
337
+ patchType: PATCH_TYPES.ASSESS_SINK,
338
+ pre: mongoSink.collectionMethodHandler,
339
+ post: mongoSink.collectionMethodPost
340
+ });
341
+
346
342
  if (semver.gte(version, '4.0.0')) {
347
343
  return mongoSink.handleVersion4(mongodb, version);
348
344
  }
@@ -358,9 +354,7 @@ module.exports = ({ common }) => {
358
354
  * It tries to get the context from the pre handler
359
355
  * See: mongoSink.collectionMethodHandler
360
356
  *
361
- * If it does not exists, it tries to create a call context
362
- * although it won't be great, it is something
363
- *
357
+ * If it does not exist, it tries to create a call context
364
358
  */
365
359
  function getCallContext(ctxt, data) {
366
360
  if (!ctxt) {
@@ -19,39 +19,97 @@ 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');
22
23
 
23
24
  ModuleHook.resolve(
24
- { name: 'mongodb', file: 'lib/mongo_client.js' },
25
- (MongoClient) => {
26
- patcher.patch(MongoClient.prototype, 'connect', {
27
- name: 'MongoClient.connect.arch_component',
28
- patchType: PATCH_TYPES.ARCH_COMPONENT,
29
- alwaysRun: true,
30
- post(ctx) {
31
- if (!ctx.result || !ctx.result.then) {
32
- return;
33
- }
25
+ {
26
+ name: 'mongodb',
27
+ file: 'lib/mongo_client.js',
28
+ version: '>=3.3.0'
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
+ }
34
40
 
35
- // We should report only when connection is successful
36
- ctx.result.then(function (client) {
37
- try {
38
- const { servers = [] } = ctx.obj.s && ctx.obj.s.options;
39
- for (const server of servers) {
40
- agentEmitter.emit('architectureComponent', {
41
- vendor: 'MongoDB',
42
- url: `mongodb://${server.host}:${server.port}`,
43
- remoteHost: '',
44
- remotePort: server.port,
45
- });
41
+ // We should report only when connection is successful
42
+ ctx.result.then(function(client) {
43
+ 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
+ });
52
+ }
53
+ } catch (err) {
54
+ logger.warn(
55
+ 'unable to report MongoDB architecture component, err: %o',
56
+ err,
57
+ );
46
58
  }
47
- } catch (err) {
48
- logger.warn(
49
- 'unable to report MongoDB architecture component, err: %o',
50
- err,
51
- );
59
+ });
60
+ },
61
+ });
62
+ }
63
+ },
64
+ );
65
+
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) */
70
+ ModuleHook.resolve(
71
+ {
72
+ name: 'mongodb',
73
+ version: '>=3.3.0'
74
+ },
75
+ (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;
52
84
  }
53
- });
54
- },
55
- });
85
+
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
+ }
102
+ }
103
+ } catch (err) {
104
+ logger.warn(
105
+ 'unable to report MongoDB architecture component, err: %o',
106
+ err,
107
+ );
108
+ }
109
+ }
110
+ });
111
+ },
112
+ });
113
+ }
56
114
  },
57
115
  );
@@ -20,6 +20,7 @@ const requireHook = require('../../../hooks/require');
20
20
  const resolveCallbackIdx = require('../../../util/callback-resolver');
21
21
  const patcher = require('../../../hooks/patcher');
22
22
  const utils = require('./utils');
23
+ const semver = require('semver');
23
24
 
24
25
  /**
25
26
  * Hooks a method to properly bind to AsyncStorage
@@ -35,10 +36,11 @@ function hookMethod(obj, method, patchName) {
35
36
  pre: (data) => {
36
37
  const { args, funcKey: identifier } = data;
37
38
  const idx = resolveCallbackIdx(data.args);
39
+
38
40
  if (idx >= 0) {
39
41
  utils.bindFnArgAtIndex({ args, idx, identifier });
40
42
  }
41
- }
43
+ },
42
44
  });
43
45
  }
44
46
 
@@ -70,34 +72,84 @@ function init() {
70
72
  requireHook.resolve(
71
73
  {
72
74
  name: 'mongodb',
73
- file: 'lib/topologies/server.js',
74
- version: '>=3.3.0 <4.0.0'
75
+ file: 'lib/topologies/topology_base.js',
76
+ version: '>=3.3.0',
75
77
  },
76
- (server) =>
77
- patcher.patch(server, {
78
- name: 'mongodb.Server',
79
- patchType: ASYNC_CONTEXT,
80
- alwaysRun: true,
81
- post: (data) => {
82
- const methods = ['command', 'insert', 'update', 'remove'];
83
- for (const method of methods) {
84
- hookMethod(data.result, method, 'mongodb.Server');
85
- }
86
- }
87
- })
78
+ (tpl, { version }) => {
79
+ if (semver.lt(version, '4.0.0')) {
80
+ return patcher.patch(tpl, 'TopologyBase', {
81
+ name: 'mongodb.TopologyBase',
82
+ patchType: ASYNC_CONTEXT,
83
+ alwaysRun: true,
84
+ post: (data) => {
85
+ const methods = ['command', 'insert', 'update', 'remove'];
86
+ for (const method of methods) {
87
+ hookMethod(data.result, method, 'mongodb.TopologyBase');
88
+ }
89
+ },
90
+ });
91
+ }
92
+ }
88
93
  );
89
94
 
90
95
  requireHook.resolve(
91
- { name: 'mongodb', file: 'lib/cursor.js', version: '>=3.3.0 <4.0.0' },
92
- (cursor) =>
93
- patcher.patch(cursor, {
94
- name: 'mongodb.Cursor',
95
- patchType: ASYNC_CONTEXT,
96
- alwaysRun: true,
97
- post: (data) => {
98
- hookMethod(data.result, '_next', 'mongodb.Cursor');
99
- }
100
- })
96
+ {
97
+ name: 'mongodb',
98
+ file: 'lib/topologies/native_topology.js',
99
+ version: '>=3.3.0',
100
+ },
101
+ (tpl, { version }) => {
102
+ if (semver.lt(version, '4.0.0')) {
103
+ return patcher.patch(tpl, {
104
+ name: 'mongodb.NativeTopology',
105
+ patchType: ASYNC_CONTEXT,
106
+ alwaysRun: true,
107
+ post: (data) => {
108
+ const methods = ['command', 'insert', 'update', 'remove'];
109
+ for (const method of methods) {
110
+ hookMethod(data.result, method, 'mongodb.NativeTopology');
111
+ }
112
+ },
113
+ });
114
+ }
115
+ }
116
+ );
117
+
118
+ requireHook.resolve(
119
+ {
120
+ name: 'mongodb',
121
+ file: 'lib/operations/command.js',
122
+ version: '>=3.3.0',
123
+ },
124
+ (command, { version }) => {
125
+ if (semver.gte(version, '4.0.0')) {
126
+ return patcher.patch(command, {
127
+ name: 'mongodb.Command',
128
+ patchType: ASYNC_CONTEXT,
129
+ alwaysRun: true,
130
+ post: (data) => {
131
+ hookMethod(data.result, 'executeCommand', 'mongodb.Command');
132
+ hookMethod(data.result, 'execute', 'mongodb.Command');
133
+ },
134
+ });
135
+ }
136
+ }
137
+ );
138
+
139
+ requireHook.resolve(
140
+ { name: 'mongodb', file: 'lib/cursor.js', version: '>=3.3.0' },
141
+ (cursor, { version }) => {
142
+ if (semver.lt(version, '4.0.0')) {
143
+ return patcher.patch(cursor, {
144
+ name: 'mongodb.Cursor',
145
+ patchType: ASYNC_CONTEXT,
146
+ alwaysRun: true,
147
+ post: (data) => {
148
+ hookMethod(data.result, '_next', 'mongodb.Cursor');
149
+ },
150
+ });
151
+ }
152
+ }
101
153
  );
102
154
  }
103
155
 
@@ -195,7 +195,7 @@ class AsyncStorage {
195
195
  }
196
196
 
197
197
  /**
198
- * Retrives the active storage context from an error
198
+ * Retrieves the active storage context from an error
199
199
  * context is added to error when one is thrown
200
200
  * see: https://github.com/Jeff-Lewis/cls-hooked/blob/master/context.js#L101
201
201
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agent",
3
- "version": "4.19.6",
3
+ "version": "4.20.1",
4
4
  "description": "Node.js security instrumentation by Contrast Security",
5
5
  "keywords": [
6
6
  "security",
@@ -133,7 +133,6 @@
133
133
  "chai": "^4.2.0",
134
134
  "chai-as-promised": "^7.1.1",
135
135
  "chai-like": "^1.1.1",
136
- "codecov": "^3.7.0",
137
136
  "config": "^3.3.3",
138
137
  "csv-writer": "^1.2.0",
139
138
  "deasync": "^0.1.24",