@contrast/agent 4.10.0 → 4.10.4

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.
package/bin/VERSION CHANGED
@@ -1 +1 @@
1
- 2.28.5
1
+ 2.28.12
Binary file
Binary file
Binary file
package/bootstrap.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  /**
3
2
  Copyright: 2022 Contrast Security, Inc
4
3
  Contact: support@contrastsecurity.com
@@ -13,6 +12,7 @@ Copyright: 2022 Contrast Security, Inc
13
12
  engineered, modified, repackaged, sold, redistributed or otherwise used in a
14
13
  way not consistent with the End User License Agreement.
15
14
  */
15
+ 'use strict';
16
16
 
17
17
  const startTime = process.hrtime();
18
18
 
@@ -25,7 +25,7 @@ const orig = Module.runMain;
25
25
  * process before invoking the main
26
26
  * script from cli
27
27
  */
28
- Module.runMain = async function(...args) {
28
+ Module.runMain = async function (...args) {
29
29
  const { isMainThread } = require('worker_threads');
30
30
 
31
31
  try {
@@ -12,9 +12,44 @@ Copyright: 2022 Contrast Security, Inc
12
12
  engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
13
  way not consistent with the End User License Agreement.
14
14
  */
15
+ const TagRange = require('../../models/tag-range');
16
+ const { CallContext, PropagationEvent, Signature } = require('../../models');
17
+
15
18
  const hasUserDefinedValidator = (data) =>
16
19
  data.obj.validators.some((validator) => validator.type === 'user defined');
17
20
 
21
+ const tagCustomValidatedValues = (values, data, tracker, tagRangeUtil) => {
22
+ for (const value of values) {
23
+ if (typeof value !== 'string') continue;
24
+
25
+ const trackingData = tracker.track(value);
26
+
27
+ if (!trackingData) return;
28
+
29
+ const { props } = trackingData;
30
+ const stringLength = value.length - 1;
31
+
32
+ props.tagRanges = tagRangeUtil.add(
33
+ props.tagRanges,
34
+ new TagRange(0, stringLength, 'custom-validated-nosql-injection')
35
+ );
36
+
37
+ props.tagRanges = tagRangeUtil.add(
38
+ props.tagRanges,
39
+ new TagRange(0, stringLength, 'string-type-checked')
40
+ );
41
+
42
+ props.event = new PropagationEvent({
43
+ context: new CallContext(data),
44
+ signature: new Signature('mongoose.mixed.doValidateSync'),
45
+ tagRanges: props.tagRanges,
46
+ source: 'P',
47
+ target: 'A',
48
+ parents: [props.event]
49
+ });
50
+ }
51
+ };
18
52
  module.exports = {
19
- hasUserDefinedValidator
53
+ hasUserDefinedValidator,
54
+ tagCustomValidatedValues
20
55
  };
@@ -15,4 +15,5 @@ Copyright: 2022 Contrast Security, Inc
15
15
  module.exports.handle = () => {
16
16
  require('./map');
17
17
  require('./string');
18
+ require('./mixed');
18
19
  };
@@ -21,9 +21,11 @@ const tagRangeUtil = require('../../models/tag-range/util');
21
21
  const {
22
22
  PATCH_TYPES: { ASSESS_PROPAGATOR }
23
23
  } = require('../../../constants');
24
- const TagRange = require('../../models/tag-range');
25
- const { CallContext, PropagationEvent, Signature } = require('../../models');
26
- const { hasUserDefinedValidator } = require('./helpers');
24
+ const {
25
+ hasUserDefinedValidator,
26
+ tagCustomValidatedValues
27
+ } = require('./helpers');
28
+ const agent = require('../../../agent');
27
29
 
28
30
  const doValidateSyncPatcher = (SchemaMap) => {
29
31
  patcher.patch(SchemaMap.prototype, 'doValidateSync', {
@@ -31,37 +33,16 @@ const doValidateSyncPatcher = (SchemaMap) => {
31
33
  name: 'mongoose.map.doValidateSync',
32
34
  patchType: ASSESS_PROPAGATOR,
33
35
  post(data) {
34
- if (data.result || data.obj.options.of.name !== 'String') return;
36
+ if (data.result) return;
35
37
 
36
38
  if (!hasUserDefinedValidator(data)) return;
37
39
 
38
- for (const value of data.args[0].values()) {
39
- const trackingData = tracker.track(value);
40
-
41
- if (!trackingData) return;
42
-
43
- const { props } = trackingData;
44
- const stringLength = value.length - 1;
45
-
46
- props.tagRanges = tagRangeUtil.add(
47
- props.tagRanges,
48
- new TagRange(0, stringLength, 'custom-validated-nosql-injection')
49
- );
50
-
51
- props.tagRanges = tagRangeUtil.add(
52
- props.tagRanges,
53
- new TagRange(0, stringLength, 'string-type-checked')
54
- );
55
-
56
- props.event = new PropagationEvent({
57
- context: new CallContext(data),
58
- signature: new Signature('mongoose.map.doValidateSync'),
59
- tagRanges: props.tagRanges,
60
- source: 'P',
61
- target: 'A',
62
- parents: [props.event]
63
- });
64
- }
40
+ tagCustomValidatedValues(
41
+ data.args[0].values(),
42
+ data,
43
+ tracker,
44
+ tagRangeUtil
45
+ );
65
46
  }
66
47
  });
67
48
  };
@@ -69,6 +50,13 @@ const doValidateSyncPatcher = (SchemaMap) => {
69
50
  requireHook.resolve(
70
51
  { name: 'mongoose', file: 'lib/schema/map.js', version: '>=5.0.0' },
71
52
  (SchemaMap) => {
53
+ if (
54
+ !agent.config ||
55
+ (agent.config && !agent.config.agent.trust_custom_validators)
56
+ ) {
57
+ return;
58
+ }
59
+
72
60
  doValidateSyncPatcher(SchemaMap);
73
61
  }
74
62
  );
@@ -0,0 +1,71 @@
1
+ /**
2
+ Copyright: 2022 Contrast Security, Inc
3
+ Contact: support@contrastsecurity.com
4
+ License: Commercial
5
+
6
+ NOTICE: This Software and the patented inventions embodied within may only be
7
+ used as part of Contrast Security’s commercial offerings. Even though it is
8
+ made available through public repositories, use of this Software is subject to
9
+ the applicable End User Licensing Agreement found at
10
+ https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ between Contrast Security and the End User. The Software may not be reverse
12
+ engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ way not consistent with the End User License Agreement.
14
+ */
15
+ 'use strict';
16
+
17
+ const tracker = require('../../../tracker');
18
+ const patcher = require('../../../hooks/patcher');
19
+ const requireHook = require('../../../hooks/require');
20
+ const tagRangeUtil = require('../../models/tag-range/util');
21
+ const {
22
+ PATCH_TYPES: { ASSESS_PROPAGATOR }
23
+ } = require('../../../constants');
24
+ const {
25
+ hasUserDefinedValidator,
26
+ tagCustomValidatedValues
27
+ } = require('./helpers');
28
+ const agent = require('../../../agent');
29
+
30
+ const doValidateSyncPatcher = (SchemaMap) => {
31
+ patcher.patch(SchemaMap.prototype, 'doValidateSync', {
32
+ alwaysRun: true,
33
+ name: 'mongoose.mixed.doValidateSync',
34
+ patchType: ASSESS_PROPAGATOR,
35
+ post(data) {
36
+ if (data.result || !hasUserDefinedValidator(data)) return;
37
+
38
+ const input = data.args[0];
39
+ const inputType = typeof input;
40
+
41
+ if (inputType !== 'string' && inputType !== 'object') return;
42
+
43
+ let values;
44
+ if (inputType === 'string') {
45
+ values = [input];
46
+ } else if (Array.isArray(input)) {
47
+ values = input;
48
+ } else if (input instanceof Map) {
49
+ values = input.values();
50
+ } else {
51
+ values = Object.values(input);
52
+ }
53
+
54
+ tagCustomValidatedValues(values, data, tracker, tagRangeUtil);
55
+ }
56
+ });
57
+ };
58
+
59
+ requireHook.resolve(
60
+ { name: 'mongoose', file: 'lib/schema/mixed.js', version: '>=5.0.0' },
61
+ (SchemaMap) => {
62
+ if (
63
+ !agent.config ||
64
+ (agent.config && !agent.config.agent.trust_custom_validators)
65
+ ) {
66
+ return;
67
+ }
68
+
69
+ doValidateSyncPatcher(SchemaMap);
70
+ }
71
+ );
@@ -19,6 +19,7 @@ const ModuleHook = require('../../hooks/require');
19
19
  const agentEmitter = require('../../agent-emitter');
20
20
  const { PATCH_TYPES } = require('../../constants');
21
21
  const logger = require('../logger')('contrast:arch-component');
22
+ const waitToConnect = require('./util');
22
23
 
23
24
  ModuleHook.resolve(
24
25
  { name: 'mysql', file: 'lib/Connection.js' },
@@ -28,22 +29,31 @@ ModuleHook.resolve(
28
29
  patchType: PATCH_TYPES.ARCH_COMPONENT,
29
30
  alwaysRun: true,
30
31
  post(wrapCtx) {
31
- try {
32
- let url = 'mysql://';
33
- if (this.config.socketPath) {
34
- url += this.config.socketPath;
35
- } else {
36
- url += `${this.config.host}`;
37
- }
38
- agentEmitter.emit('architectureComponent', {
39
- vendor: 'MySQL',
40
- url: new URL(url).toString(),
41
- remoteHost: '',
42
- remotePort: !this.config.socketPath ? this.config.port : -1
32
+ waitToConnect(wrapCtx)
33
+ .then(() => {
34
+ try {
35
+ let url = 'mysql://';
36
+ if (this.config.socketPath) {
37
+ url += this.config.socketPath;
38
+ } else {
39
+ url += `${this.config.host}`;
40
+ }
41
+ agentEmitter.emit('architectureComponent', {
42
+ vendor: 'MySQL',
43
+ url: new URL(url).toString(),
44
+ remoteHost: '',
45
+ remotePort: !this.config.socketPath ? this.config.port : -1
46
+ });
47
+ } catch (err) {
48
+ logger.warn(
49
+ 'unable to report MySQL architecture component\n',
50
+ err
51
+ );
52
+ }
53
+ })
54
+ .catch((err) => {
55
+ logger.warn('unable to report MySQL architecture component\n', err);
43
56
  });
44
- } catch (err) {
45
- logger.warn('unable to report MySQL architecture component\n', err);
46
- }
47
57
  }
48
58
  });
49
59
  }
@@ -18,6 +18,7 @@ const ModuleHook = require('../../hooks/require');
18
18
  const agentEmitter = require('../../agent-emitter');
19
19
  const { PATCH_TYPES } = require('../../constants');
20
20
  const logger = require('../logger')('contrast:arch-component');
21
+ const waitToConnect = require('./util');
21
22
 
22
23
  ModuleHook.resolve({ name: 'pg', file: 'lib/client.js' }, (pgClient) =>
23
24
  patcher.patch(pgClient, {
@@ -25,37 +26,46 @@ ModuleHook.resolve({ name: 'pg', file: 'lib/client.js' }, (pgClient) =>
25
26
  patchType: PATCH_TYPES.ARCH_COMPONENT,
26
27
  alwaysRun: true,
27
28
  post(wrapCtx) {
28
- try {
29
- const {
30
- host = process.env.PGHOST,
31
- port = process.env.PGPORT
32
- } = wrapCtx.result;
29
+ waitToConnect(wrapCtx)
30
+ .then(() => {
31
+ try {
32
+ const {
33
+ host = process.env.PGHOST,
34
+ port = process.env.PGPORT
35
+ } = wrapCtx.result;
33
36
 
34
- if (!host) {
35
- return;
36
- }
37
+ if (!host) {
38
+ return;
39
+ }
37
40
 
38
- let url = host;
41
+ let url = host;
39
42
 
40
- // build protocol and port into url prior to parsing
41
- if (url.indexOf('://') === -1) {
42
- url = `postgresql://${url}`;
43
- }
44
- if (port !== undefined) {
45
- url = `${url}:${port}`;
46
- }
43
+ // build protocol and port into url prior to parsing
44
+ if (url.indexOf('://') === -1) {
45
+ url = `postgresql://${url}`;
46
+ }
47
+ if (port !== undefined) {
48
+ url = `${url}:${port}`;
49
+ }
47
50
 
48
- agentEmitter.emit('architectureComponent', {
49
- vendor: 'PostgreSQL',
50
- remotePort: port || 0,
51
- url: new URL(url).toString()
51
+ agentEmitter.emit('architectureComponent', {
52
+ vendor: 'PostgreSQL',
53
+ remotePort: port || 0,
54
+ url: new URL(url).toString()
55
+ });
56
+ } catch (err) {
57
+ logger.warn(
58
+ 'unable to report PostgreSQL architecture component\n%o',
59
+ err
60
+ );
61
+ }
62
+ })
63
+ .catch((err) => {
64
+ logger.warn(
65
+ 'unable to report PostgreSQL architecture component\n%o',
66
+ err
67
+ );
52
68
  });
53
- } catch (err) {
54
- logger.warn(
55
- 'unable to report PostgreSQL architecture component\n%o',
56
- err
57
- );
58
- }
59
69
  }
60
70
  })
61
71
  );
@@ -0,0 +1,49 @@
1
+ /**
2
+ Copyright: 2022 Contrast Security, Inc
3
+ Contact: support@contrastsecurity.com
4
+ License: Commercial
5
+
6
+ NOTICE: This Software and the patented inventions embodied within may only be
7
+ used as part of Contrast Security’s commercial offerings. Even though it is
8
+ made available through public repositories, use of this Software is subject to
9
+ the applicable End User Licensing Agreement found at
10
+ https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ between Contrast Security and the End User. The Software may not be reverse
12
+ engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ way not consistent with the End User License Agreement.
14
+ */
15
+ const MYSQL = 'mysql.connect.arch_component';
16
+ const POSTGRES = 'pg.Client.arch_component';
17
+
18
+ module.exports = function waitToConnect(ctx, count = 0) {
19
+ return new Promise((resolve, reject) => {
20
+ const maxAttempts = 10 * 60; // i.e., 1 min.
21
+ const checkConnection = setInterval(
22
+ function(ctx, resolve, reject) {
23
+ if (count >= maxAttempts) {
24
+ clearInterval(checkConnection);
25
+ reject();
26
+ }
27
+ switch (ctx.name) {
28
+ case MYSQL:
29
+ if (ctx.obj.state === 'authenticated') {
30
+ clearInterval(checkConnection);
31
+ resolve();
32
+ }
33
+ break;
34
+ case POSTGRES:
35
+ if (ctx.result._connected) {
36
+ clearInterval(checkConnection);
37
+ resolve();
38
+ }
39
+ break;
40
+ }
41
+ count++;
42
+ },
43
+ 100,
44
+ ctx,
45
+ resolve,
46
+ reject
47
+ );
48
+ });
49
+ };
@@ -26,7 +26,7 @@ const { isString } = require('../../util/is-string');
26
26
  const { emitSendEvent } = require('../../hooks/frameworks/common');
27
27
  const {
28
28
  prototype: { decorateRequest },
29
- setFrameworkRequest
29
+ setFrameworkRequest,
30
30
  } = require('../../hooks/frameworks/base');
31
31
  const ExpressRequest = require('../../reporter/models/frameworks/express-request');
32
32
 
@@ -40,7 +40,7 @@ const {
40
40
  INPUT_TYPES,
41
41
  SINK_TYPES,
42
42
  MW_PATH,
43
- LAYER_STACK
43
+ LAYER_STACK,
44
44
  } = constants;
45
45
  const { EVENTS } = Helpers;
46
46
 
@@ -111,14 +111,14 @@ class ExpressFramework {
111
111
  agentEmitter.emit(
112
112
  EVENTS.REQUEST_SEND,
113
113
  data.args[0],
114
- SINK_TYPES.RESPONSE_BODY
114
+ SINK_TYPES.RESPONSE_BODY,
115
115
  );
116
116
 
117
- const body = data.args[0];
117
+ const [body] = data.args;
118
118
  if (isString(body)) {
119
119
  emitSendEvent(body.valueOf());
120
120
  }
121
- }
121
+ },
122
122
  });
123
123
 
124
124
  patcher.patch(express.response, 'push', {
@@ -128,14 +128,14 @@ class ExpressFramework {
128
128
  agentEmitter.emit(
129
129
  EVENTS.REQUEST_SEND,
130
130
  data.args[0],
131
- SINK_TYPES.RESPONSE_BODY
131
+ SINK_TYPES.RESPONSE_BODY,
132
132
  );
133
133
 
134
- const body = data.args[0];
134
+ const [body] = data.args;
135
135
  if (isString(body)) {
136
136
  emitSendEvent(body.valueOf());
137
137
  }
138
- }
138
+ },
139
139
  });
140
140
 
141
141
  patcher.patch(express.response, 'end', {
@@ -145,9 +145,9 @@ class ExpressFramework {
145
145
  agentEmitter.emit(
146
146
  EVENTS.REQUEST_SEND,
147
147
  data.args[0],
148
- SINK_TYPES.RESPONSE_BODY
148
+ SINK_TYPES.RESPONSE_BODY,
149
149
  );
150
- }
150
+ },
151
151
  });
152
152
 
153
153
  patcher.patch(express.Router, 'use', {
@@ -157,7 +157,7 @@ class ExpressFramework {
157
157
  pre: self.injectContrastMiddleware.bind(self),
158
158
  post: (wrapCtx) => {
159
159
  agentEmitter.emit(EVENTS.ROUTER_USE, self, wrapCtx);
160
- }
160
+ },
161
161
  });
162
162
 
163
163
  patcher.patch(express.Router, 'route', {
@@ -166,7 +166,7 @@ class ExpressFramework {
166
166
  alwaysRun: true,
167
167
  post: (wrapCtx) => {
168
168
  agentEmitter.emit(EVENTS.ROUTER_ROUTE, self, wrapCtx);
169
- }
169
+ },
170
170
  });
171
171
 
172
172
  LC_HTTP_VERBS.forEach((verb) => {
@@ -177,7 +177,7 @@ class ExpressFramework {
177
177
  pre: self.injectContrastMiddleware.bind(self),
178
178
  post: (wrapCtx) => {
179
179
  agentEmitter.emit(EVENTS.ROUTER_METHOD, self, wrapCtx, verb);
180
- }
180
+ },
181
181
  });
182
182
  });
183
183
 
@@ -188,7 +188,7 @@ class ExpressFramework {
188
188
  pre: self.injectContrastMiddleware.bind(self),
189
189
  post: (wrapCtx) => {
190
190
  agentEmitter.emit(EVENTS.ROUTER_METHOD, self, wrapCtx, 'all');
191
- }
191
+ },
192
192
  });
193
193
 
194
194
  patcher.patch(express.application, 'use', {
@@ -198,7 +198,7 @@ class ExpressFramework {
198
198
  pre: self.injectContrastMiddleware.bind(self),
199
199
  post: (wrapCtx) => {
200
200
  agentEmitter.emit(EVENTS.APP_USE, self, wrapCtx);
201
- }
201
+ },
202
202
  });
203
203
 
204
204
  LC_HTTP_VERBS.forEach((verb) => {
@@ -209,7 +209,7 @@ class ExpressFramework {
209
209
  pre: self.injectContrastMiddleware.bind(self),
210
210
  post: (wrapCtx) => {
211
211
  agentEmitter.emit(EVENTS.APP_METHOD, self, wrapCtx, verb);
212
- }
212
+ },
213
213
  });
214
214
  });
215
215
 
@@ -220,7 +220,7 @@ class ExpressFramework {
220
220
  pre: self.injectContrastMiddleware.bind(self),
221
221
  post: (wrapCtx) => {
222
222
  agentEmitter.emit(EVENTS.APP_METHOD, self, wrapCtx, 'all');
223
- }
223
+ },
224
224
  });
225
225
 
226
226
  agentEmitter.on(HTTP_EVENTS.SERVER_CREATE, this.onServerCreate.bind(this));
@@ -238,7 +238,7 @@ class ExpressFramework {
238
238
  if (!app || !app.defaultConfiguration) {
239
239
  logger.error(
240
240
  `non-express application mistakenly registered`,
241
- new Error().stack
241
+ new Error().stack,
242
242
  );
243
243
  return;
244
244
  }
@@ -305,7 +305,7 @@ class ExpressFramework {
305
305
  EVENTS.REQUEST_READY,
306
306
  req,
307
307
  res,
308
- INPUT_TYPES.QUERYSTRING
308
+ INPUT_TYPES.QUERYSTRING,
309
309
  );
310
310
  next();
311
311
  }, 'query');
@@ -322,6 +322,14 @@ class ExpressFramework {
322
322
  next();
323
323
  }, 'rawParser');
324
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
+
325
333
  this.useAfter(function ContrastTextBodyParsed(req, res, next) {
326
334
  agentEmitter.emit(EVENTS.BODY_PARSED, req, res, INPUT_TYPES.BODY);
327
335
  next();
@@ -329,7 +337,7 @@ class ExpressFramework {
329
337
 
330
338
  this.useAfter(function ContrastBodyParsed(req, res, next) {
331
339
  agentEmitter.emit(EVENTS.BODY_PARSED, req, res, {
332
- type: INPUT_TYPES.BODY
340
+ type: INPUT_TYPES.BODY,
333
341
  });
334
342
  next();
335
343
  }, 'urlencodedParser');
@@ -345,7 +353,7 @@ class ExpressFramework {
345
353
  EVENTS.COOKIES_PARSED,
346
354
  req,
347
355
  res,
348
- INPUT_TYPES.COOKIE_VALUE
356
+ INPUT_TYPES.COOKIE_VALUE,
349
357
  );
350
358
 
351
359
  next();
@@ -395,7 +403,7 @@ class ExpressFramework {
395
403
  patchType: PATCH_TYPES.SOURCE,
396
404
  pre(data) {
397
405
  req[LAYER_STACK].pop();
398
- }
406
+ },
399
407
  });
400
408
  if (req.query) {
401
409
  decorateRequest({ query: req.query });
@@ -406,11 +414,11 @@ class ExpressFramework {
406
414
  EVENTS.PARAM_PARSED,
407
415
  req,
408
416
  res,
409
- INPUT_TYPES.URL_PARAMETER
417
+ INPUT_TYPES.URL_PARAMETER,
410
418
  );
411
419
 
412
420
  decorateRequest({
413
- parameters: req.params
421
+ parameters: req.params,
414
422
  });
415
423
  }
416
424
 
@@ -422,21 +430,18 @@ class ExpressFramework {
422
430
 
423
431
  Whatever the core issue is, it doesn't appear to have any effects
424
432
  elsewhere in any of our Express/Kraken framework support.
425
-
426
- BODY_PARSED event is emitted to support Sails framework
427
433
  */
428
434
  if (req.body) {
429
435
  decorateRequest({ body: req.body });
430
- agentEmitter.emit(EVENTS.BODY_PARSED, req, res, req.body);
431
436
  }
432
- }
437
+ },
433
438
  });
434
439
  }
435
440
  }
436
441
 
437
442
  useAfter(fn, after) {
438
443
  this.handlers[after] = {
439
- post: fn
444
+ post: fn,
440
445
  };
441
446
  }
442
447
 
@@ -108,9 +108,10 @@ class DebugLogFactory {
108
108
 
109
109
  // We always log to a file, but check whether we should log to stdout
110
110
  if (
111
- !this.stdout ||
112
- !process.env.DEBUG ||
113
- !/(^|,\s*)contrast:.+/.test(process.env.DEBUG)
111
+ this.loggerPath &&
112
+ (!this.stdout ||
113
+ !process.env.DEBUG ||
114
+ !/(^|,\s*)contrast:.+/.test(process.env.DEBUG))
114
115
  ) {
115
116
  this.mute = true;
116
117
  }
@@ -15,7 +15,6 @@ Copyright: 2022 Contrast Security, Inc
15
15
  'use strict';
16
16
 
17
17
  const t = require('@babel/types');
18
- const _ = require('lodash');
19
18
 
20
19
  /**
21
20
  * Wraps binary expressions in one of the following: `ContrastMethods.__add`,
@@ -30,7 +29,7 @@ const _ = require('lodash');
30
29
  * @param {import('.').State} state
31
30
  */
32
31
  module.exports = function BinaryExpression(path, state) {
33
- const spec = _.find(state.specs, { token: path.node.operator });
32
+ const spec = state.specs.find(({ token }) => token === path.node.operator);
34
33
  if (
35
34
  !spec ||
36
35
  !state.callees[spec.name] ||
@@ -15,7 +15,6 @@ Copyright: 2022 Contrast Security, Inc
15
15
  'use strict';
16
16
 
17
17
  const { expression } = require('@babel/template');
18
- const _ = require('lodash');
19
18
 
20
19
  /**
21
20
  * @typedef {Object} Spec
@@ -132,7 +131,9 @@ module.exports = function createCallees(agent) {
132
131
  const callees = specs.reduce(
133
132
  (memo, spec) =>
134
133
  (assessMode && spec.modes.assess) || (protectMode && spec.modes.protect)
135
- ? _.set(memo, spec.name, calleeBuilder({ name: spec.name }))
134
+ ? Object.assign(memo, {
135
+ [spec.name]: calleeBuilder({ name: spec.name })
136
+ })
136
137
  : memo,
137
138
  {}
138
139
  );
@@ -13,7 +13,6 @@ Copyright: 2022 Contrast Security, Inc
13
13
  way not consistent with the End User License Agreement.
14
14
  */
15
15
  const { statement } = require('@babel/template');
16
- const _ = require('lodash');
17
16
 
18
17
  const logStatementBuilder = statement(
19
18
  `if (global.CONTRAST_LOG) {
@@ -38,7 +37,10 @@ const logStatementBuilder = statement(
38
37
  * @param {import('.').State} state
39
38
  */
40
39
  module.exports = function CatchClause(path, state) {
41
- if (!_.get(state.agent, 'config.agent.node.enable_catch_log')) return;
40
+ const { config } = state.agent;
41
+ if (!config || !config.agent.node.enable_catch_log) {
42
+ return;
43
+ }
42
44
 
43
45
  path.node.param = path.node.param || path.scope.generateUidIdentifier('err');
44
46
  path
@@ -15,7 +15,6 @@ Copyright: 2022 Contrast Security, Inc
15
15
  'use strict';
16
16
 
17
17
  const t = require('@babel/types');
18
- const _ = require('lodash');
19
18
 
20
19
  const IMPORT_META_URL_MEMBER_EXPRESSION = t.memberExpression(
21
20
  t.memberExpression(t.identifier('import'), t.identifier('meta')),
@@ -49,7 +48,8 @@ module.exports = function ImportDeclaration(path, state) {
49
48
 
50
49
  path.insertAfter(
51
50
  specifiers.map((importSpec) => {
52
- const spec = _.find(state.specs, { type: importSpec.type });
51
+ const spec = state.specs.find(({ type }) => type === importSpec.type);
52
+
53
53
  if (!spec || !state.callees[spec.name]) return;
54
54
 
55
55
  const args = [importSpec.local];
@@ -116,6 +116,7 @@ class Rewriter {
116
116
  null,
117
117
  initialState
118
118
  );
119
+ traverse.cache.clear();
119
120
  }
120
121
 
121
122
  /**
@@ -14,7 +14,6 @@ Copyright: 2022 Contrast Security, Inc
14
14
  */
15
15
  'use strict';
16
16
 
17
- const _ = require('lodash');
18
17
  const logger = require('../logger')('contrast:rewrite:injections');
19
18
  const patcher = require('../../hooks/patcher');
20
19
  const { PATCH_TYPES } = require('../../constants');
@@ -161,8 +160,7 @@ module.exports = {
161
160
  * @returns {Injection[]}
162
161
  */
163
162
  getEnabled() {
164
- return _.reduce(
165
- injections,
163
+ return Object.values(injections).reduce(
166
164
  (enabled, injection) =>
167
165
  injection.enabled() ? [...enabled, injection] : enabled,
168
166
  []
@@ -13,7 +13,6 @@ Copyright: 2022 Contrast Security, Inc
13
13
  way not consistent with the End User License Agreement.
14
14
  */
15
15
  'use strict';
16
- const _ = require('lodash');
17
16
 
18
17
  /**
19
18
  * Helper class that provides some means of optimizing the rewrite process.
@@ -32,11 +31,9 @@ module.exports = class RewriteLog {
32
31
  this._tokenMatches = specs.reduce(
33
32
  (matches, spec) =>
34
33
  callees[spec.name]
35
- ? _.set(
36
- matches,
37
- spec.name,
38
- !spec.token || 0 <= codeString.indexOf(spec.token)
39
- )
34
+ ? Object.assign(matches, {
35
+ [spec.name]: !spec.token || 0 <= codeString.indexOf(spec.token)
36
+ })
40
37
  : matches,
41
38
  {}
42
39
  );
@@ -60,7 +57,7 @@ module.exports = class RewriteLog {
60
57
  * if we need to even rewrite the code at all.
61
58
  */
62
59
  foundTokens() {
63
- return _.some(this._tokenMatches, _.identity);
60
+ return Object.values(this._tokenMatches).some(Boolean);
64
61
  }
65
62
 
66
63
  /**
@@ -72,6 +69,6 @@ module.exports = class RewriteLog {
72
69
  * make changes, we can just return original code.
73
70
  */
74
71
  rewritesOccurred() {
75
- return !this.aborted && _.some(this._tokensRewritten, _.identity);
72
+ return !this.aborted && Object.values(this._tokensRewritten).some(Boolean);
76
73
  }
77
74
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agent",
3
- "version": "4.10.0",
3
+ "version": "4.10.4",
4
4
  "description": "Node.js security instrumentation by Contrast Security",
5
5
  "keywords": [
6
6
  "security",
@@ -113,12 +113,14 @@
113
113
  "devDependencies": {
114
114
  "@aws-sdk/client-dynamodb": "^3.39.0",
115
115
  "@bmacnaughton/string-generator": "^1.0.0",
116
- "@contrast/eslint-config": "^2.0.1",
116
+ "@contrast/eslint-config": "^2.2.0",
117
117
  "@contrast/fake-module": "file:test/mock/contrast-fake",
118
118
  "@contrast/screener-service": "^1.12.9",
119
119
  "@hapi/boom": "file:test/mock/boom",
120
120
  "@hapi/hapi": "file:test/mock/hapi",
121
121
  "@ls-lint/ls-lint": "^1.8.1",
122
+ "@typescript-eslint/eslint-plugin": "^5.10.2",
123
+ "@typescript-eslint/parser": "^5.10.2",
122
124
  "ajv": "^8.5.0",
123
125
  "ast-types": "^0.12.4",
124
126
  "aws-sdk": "file:test/mock/aws-sdk",
@@ -135,11 +137,11 @@
135
137
  "dustjs-linkedin": "^3.0.1",
136
138
  "ejs": "^3.1.6",
137
139
  "escape-html": "^1.0.3",
138
- "eslint": "^8.2.0",
139
- "eslint-config-prettier": "^6.11.0",
140
- "eslint-plugin-mocha": "^7.0.1",
140
+ "eslint": "^8.8.0",
141
+ "eslint-config-prettier": "^8.3.0",
142
+ "eslint-plugin-mocha": "^10.0.3",
141
143
  "eslint-plugin-node": "^11.1.0",
142
- "eslint-plugin-prettier": "^3.1.4",
144
+ "eslint-plugin-prettier": "^4.0.0",
143
145
  "express": "file:test/mock/express",
144
146
  "fetch-cookie": "^0.11.0",
145
147
  "form-data": "^3.0.0",
@@ -168,7 +170,7 @@
168
170
  "nyc": "^15.1.0",
169
171
  "pg": "file:test/mock/pg",
170
172
  "pino": "^6.7.0",
171
- "prettier": "^1.19.1",
173
+ "prettier": "^2.5.1",
172
174
  "proxyquire": "^2.1.0",
173
175
  "qs": "^6.9.4",
174
176
  "rethinkdb": "file:test/mock/rethinkdb",