@contrast/agent 4.8.0 → 4.10.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.
Files changed (59) hide show
  1. package/bin/VERSION +1 -1
  2. package/bin/linux/contrast-service +0 -0
  3. package/bin/mac/contrast-service +0 -0
  4. package/bin/windows/contrast-service.exe +0 -0
  5. package/bootstrap.js +12 -2
  6. package/esm.mjs +33 -0
  7. package/lib/assess/index.js +2 -0
  8. package/lib/assess/models/source-event.js +6 -0
  9. package/lib/assess/policy/rules.json +29 -0
  10. package/lib/assess/policy/signatures.json +6 -0
  11. package/lib/assess/propagators/JSON/stringify.js +77 -7
  12. package/lib/assess/propagators/joi/any.js +48 -0
  13. package/lib/assess/propagators/joi/index.js +2 -0
  14. package/lib/assess/propagators/joi/object.js +61 -0
  15. package/lib/assess/propagators/joi/string-base.js +16 -0
  16. package/lib/assess/propagators/mongoose/helpers.js +36 -1
  17. package/lib/assess/propagators/mongoose/index.js +1 -0
  18. package/lib/assess/propagators/mongoose/map.js +19 -31
  19. package/lib/assess/propagators/mongoose/mixed.js +71 -0
  20. package/lib/assess/propagators/mongoose/string.js +8 -0
  21. package/lib/assess/sinks/rethinkdb-nosql-injection.js +142 -0
  22. package/lib/assess/sources/event-handler.js +307 -0
  23. package/lib/assess/sources/index.js +93 -5
  24. package/lib/assess/spdy/index.js +23 -0
  25. package/lib/assess/spdy/sinks/index.js +23 -0
  26. package/lib/assess/spdy/sinks/xss.js +84 -0
  27. package/lib/assess/technologies/index.js +2 -1
  28. package/lib/constants.js +2 -1
  29. package/lib/contrast.js +6 -6
  30. package/lib/core/arch-components/index.js +1 -0
  31. package/lib/core/arch-components/mongodb.js +22 -18
  32. package/lib/core/arch-components/mysql.js +25 -15
  33. package/lib/core/arch-components/postgres.js +40 -12
  34. package/lib/core/arch-components/sqlite3.js +3 -5
  35. package/lib/core/arch-components/util.js +49 -0
  36. package/lib/core/config/options.js +37 -1
  37. package/lib/core/exclusions/exclusion.js +2 -5
  38. package/lib/core/express/index.js +28 -2
  39. package/lib/core/express/utils.js +8 -3
  40. package/lib/core/fastify/index.js +2 -1
  41. package/lib/core/hapi/index.js +2 -1
  42. package/lib/core/koa/index.js +9 -1
  43. package/lib/core/rewrite/callees.js +16 -0
  44. package/lib/core/rewrite/import-declaration.js +71 -0
  45. package/lib/core/rewrite/index.js +9 -7
  46. package/lib/core/rewrite/injections.js +5 -1
  47. package/lib/hooks/frameworks/index.js +2 -0
  48. package/lib/hooks/frameworks/spdy.js +87 -0
  49. package/lib/hooks/http.js +11 -0
  50. package/lib/protect/restify/sources.js +35 -0
  51. package/lib/protect/rules/nosqli/nosql-injection-rule.js +30 -16
  52. package/lib/protect/rules/nosqli/nosql-scanner/index.js +1 -1
  53. package/lib/protect/rules/nosqli/nosql-scanner/rethinkdbscanner.js +26 -0
  54. package/lib/protect/sinks/index.js +2 -0
  55. package/lib/protect/sinks/mongodb.js +1 -3
  56. package/lib/protect/sinks/rethinkdb.js +47 -0
  57. package/lib/reporter/translations/to-protobuf/dtm/trace-event/index.js +4 -4
  58. package/lib/util/source-map.js +3 -3
  59. package/package.json +18 -12
package/bin/VERSION CHANGED
@@ -1 +1 @@
1
- 2.28.5
1
+ 2.28.9
Binary file
Binary file
Binary file
package/bootstrap.js CHANGED
@@ -26,7 +26,16 @@ const orig = Module.runMain;
26
26
  * script from cli
27
27
  */
28
28
  Module.runMain = async function(...args) {
29
+ const { isMainThread } = require('worker_threads');
30
+
29
31
  try {
32
+ // Skip instrumenting worker threads.
33
+ if (!isMainThread) {
34
+ orig.apply(this, args);
35
+
36
+ return;
37
+ }
38
+
30
39
  const { enabled } = await loader.init(process.argv);
31
40
  if (enabled) {
32
41
  await loader.bootstrap(process.argv);
@@ -37,9 +46,10 @@ Module.runMain = async function(...args) {
37
46
  orig.apply(this, args);
38
47
  loader.logTime(appStartTime, 'application');
39
48
  loader.logTime(startTime, 'agent & application');
40
-
41
- } catch(err) {
49
+ } catch (err) {
50
+ // eslint-disable-next-line no-console
42
51
  console.error(err);
52
+ // eslint-disable-next-line no-process-exit
43
53
  process.exit(-1);
44
54
  }
45
55
  };
package/esm.mjs CHANGED
@@ -22,6 +22,7 @@ if (enabled) {
22
22
  await loader.bootstrap(process.argv);
23
23
  }
24
24
  await loader.resetArgs(process.argv[0], process.argv[1]);
25
+ const { readFile } = require('fs').promises;
25
26
 
26
27
  const agent = require(`./lib/agent.js`);
27
28
  const logger = require(`./lib/core/logger/index.js`)('contrast:esm-loaders');
@@ -95,3 +96,35 @@ export async function transformSource(source, context, defaultTransformSource) {
95
96
  return defaultTransformSource(source, context, defaultTransformSource);
96
97
  }
97
98
  }
99
+
100
+ /**
101
+ * For Node 16 and above, the 'getFormat', 'getSource' and 'transformSource' have been
102
+ * consolidated into one 'load' hook. The logic is similar to that of transformSource
103
+ * except that the source is not provided and must be either read in from the file provided
104
+ * or accessed from the cache.
105
+ *
106
+ * @see https://nodejs.org/dist/latest-v16.x/docs/api/esm.html#loadurl-context-defaultload
107
+ * @param {string} url
108
+ * @param {{ format: string, url: string }} context
109
+ * @param {Function} defaultLoad
110
+ * @returns {Promise<{ format: string, source: string | SharedArrayBuffer | Uint8Array }>}
111
+ */
112
+ export async function load(url, context, defaultLoad) {
113
+ const filename = fileURLToPath(url);
114
+
115
+ try {
116
+ const cached = helpers.find(agent, filename);
117
+ const source = cached || await readFile(filename, 'utf8');
118
+ const result = rewriter.rewriteFile(source, filename, { sourceType: 'module' });
119
+ helpers.cacheWithSourceMap(agent, filename, result);
120
+ return { format: context.format, source: result.code };
121
+
122
+ } catch (err) {
123
+ logger.error(
124
+ 'Failed to load rewritten code for %s, err: %o, rewritten code %s, loading original code.',
125
+ filename,
126
+ err
127
+ );
128
+ return defaultLoad(url, context, defaultLoad);
129
+ }
130
+ }
@@ -20,6 +20,7 @@ const KoaFrameworkInstrumentation = require('./koa');
20
20
  const HapiFrameworkInstrumentation = require('./hapi');
21
21
  const Loopback4FrameworkInstrumentation = require('./loopback4');
22
22
  const ExpressFrameworkInstrumentation = require('./express');
23
+ const SpdyFrameworkInstrumentation = require('./spdy');
23
24
  const assessSources = require('./sources');
24
25
 
25
26
  module.exports = function(agent) {
@@ -29,5 +30,6 @@ module.exports = function(agent) {
29
30
  new HapiFrameworkInstrumentation(agent);
30
31
  new Loopback4FrameworkInstrumentation(agent);
31
32
  new ExpressFrameworkInstrumentation(agent);
33
+ new SpdyFrameworkInstrumentation(agent);
32
34
  assessSources.init(agent);
33
35
  };
@@ -32,6 +32,7 @@ module.exports = class SourceEvent extends BaseEvent {
32
32
  * @param {boolean} opts.isArray true if req.body is an array (from JSON body).
33
33
  */
34
34
  constructor({
35
+ code,
35
36
  context,
36
37
  name,
37
38
  signature,
@@ -43,6 +44,7 @@ module.exports = class SourceEvent extends BaseEvent {
43
44
  eventSources = [{ sourceType: type.toUpperCase(), sourceName: name }]
44
45
  } = {}) {
45
46
  super(context, signature, tagRanges);
47
+ this.code = code;
46
48
  this.target = target;
47
49
  this.eventSources = eventSources;
48
50
  this.parents = [];
@@ -65,6 +67,10 @@ module.exports = class SourceEvent extends BaseEvent {
65
67
  mapCustomMethods() {
66
68
  const type = this.eventSources[0].sourceType.toLowerCase();
67
69
  const name = this.eventSources[0].sourceName;
70
+ if (this.code) {
71
+ return;
72
+ }
73
+
68
74
  if (this.isRequestObject) {
69
75
  this.code = `const ${type} = req.${type}`;
70
76
  } else if (this.isArray) {
@@ -1376,6 +1376,11 @@
1376
1376
  "type": "http",
1377
1377
  "provider": "./sinks/dustjs-linkedin-xss"
1378
1378
  },
1379
+ "nosql-injection-rethinkdb": {
1380
+ "enabled": true,
1381
+ "type": "http",
1382
+ "provider": "./sinks/rethinkdb-nosql-injection"
1383
+ },
1379
1384
  "reflected-xss": {
1380
1385
  "enabled": true,
1381
1386
  "type": "hook",
@@ -1404,6 +1409,30 @@
1404
1409
  ]
1405
1410
  }
1406
1411
  },
1412
+ "express.response.push": {
1413
+ "type": "dataflow",
1414
+ "enabled": true,
1415
+ "stackTrustedLibs": [],
1416
+ "conditions": {
1417
+ "mode": "or",
1418
+ "args": [
1419
+ {
1420
+ "index": 0,
1421
+ "disallowedTags": [
1422
+ "cookie",
1423
+ "header",
1424
+ "limited-chars",
1425
+ "alphanum-space-hyphen",
1426
+ "html-encoded",
1427
+ "sql-encoded",
1428
+ "url-encoded",
1429
+ "weak-url-encoded"
1430
+ ],
1431
+ "requiredTags": ["untrusted"]
1432
+ }
1433
+ ]
1434
+ }
1435
+ },
1407
1436
  "swig.compile": {
1408
1437
  "type": "dataflow",
1409
1438
  "enabled": true,
@@ -254,6 +254,12 @@
254
254
  "methodName": "response.send",
255
255
  "isModule": true
256
256
  },
257
+ "express.response.push": {
258
+ "moduleName": "express",
259
+ "version": ">=4.0.0",
260
+ "methodName": "response.push",
261
+ "isModule": true
262
+ },
257
263
  "express.response.set": {
258
264
  "moduleName": "express",
259
265
  "version": ">=4.0.0",
@@ -23,10 +23,16 @@ const crypto = require('crypto');
23
23
  const TagRange = require('../../models/tag-range');
24
24
  const tracker = require('../../../tracker.js');
25
25
  const { isString } = require('../../../util/is-string');
26
- const { PropagationEvent, Signature, CallContext } = require('../../models');
26
+ const {
27
+ PropagationEvent,
28
+ Signature,
29
+ CallContext,
30
+ SourceEvent
31
+ } = require('../../models');
27
32
  const ContrastJSON = require('../../../core/rewrite/injections').get('JSON');
28
33
  const Debraner = require('../../membrane/debraner');
29
34
  const { PROXY_TARGET } = require('../../../constants');
35
+ const { trackedObjects } = require('../../sources/event-handler');
30
36
 
31
37
  const sig = new Signature({
32
38
  moduleName: 'JSON',
@@ -125,6 +131,7 @@ function patchReplacer(replacer) {
125
131
  }
126
132
 
127
133
  let patched = false;
134
+
128
135
  module.exports.handle = function() {
129
136
  if (patched) {
130
137
  return;
@@ -136,7 +143,7 @@ module.exports.handle = function() {
136
143
  patchType: PATCH_TYPES.ASSESS_PROPAGATOR,
137
144
 
138
145
  pre(data) {
139
- const [, , space] = data.args;
146
+ const [obj, , space] = data.args;
140
147
  let [input, replacer] = data.args;
141
148
  replacer = patchReplacer(replacer);
142
149
 
@@ -148,11 +155,24 @@ module.exports.handle = function() {
148
155
  spaceProps: undefined,
149
156
  target: undefined,
150
157
  membrane: undefined,
151
- debraner: undefined
158
+ debraner: undefined,
159
+ sourcesMap: {}
152
160
  };
153
161
 
162
+ if (trackedObjects.has(obj)) {
163
+ const { type } = trackedObjects.get(obj);
164
+ data.metadata.sourcesMap[type] = obj;
165
+ }
166
+
154
167
  // is the argument behind a membrane? try getting { target, membrane }.
155
168
  const braned = input && input[PROXY_TARGET];
169
+
170
+ const isSourceObject = input && trackedObjects.has(input);
171
+
172
+ if (isSourceObject) {
173
+ data.metadata.propagate = true;
174
+ }
175
+
156
176
  // next two are used by the replacer function
157
177
  let safeKeys;
158
178
  if (braned) {
@@ -183,7 +203,15 @@ module.exports.handle = function() {
183
203
  * @param {string} key
184
204
  * @param {string} value
185
205
  */
206
+ // eslint-disable-next-line complexity
186
207
  function contrastReplacer(key, val) {
208
+ if (trackedObjects.has(obj)) {
209
+ const { type } = trackedObjects.get(obj);
210
+ if (!data.metadata.sourcesMap[type]) {
211
+ data.metadata.sourcesMap[type] = obj;
212
+ }
213
+ }
214
+
187
215
  let isTracked = false;
188
216
  const valProperties = tracker.getData(val);
189
217
  if (valProperties && valProperties.tagRanges.length) {
@@ -206,7 +234,7 @@ module.exports.handle = function() {
206
234
  // make skeletal version of valProperties with only the tagRanges prop.
207
235
  data.metadata[id] =
208
236
  // eslint-disable-next-line prettier/prettier
209
- { tagRanges: [new TagRange(0, value.length - 1, '')] };
237
+ { tagRanges: [new TagRange(0, value.length - 1, '')] };
210
238
  value = `${canary}${id}~${value}`;
211
239
  id++;
212
240
  }
@@ -229,6 +257,7 @@ module.exports.handle = function() {
229
257
  if (!data.metadata.propagate) {
230
258
  return data.result;
231
259
  }
260
+
232
261
  // restore the proxies to the argument
233
262
  const { debraner } = data.metadata;
234
263
  if (debraner) {
@@ -262,6 +291,41 @@ module.exports.handle = function() {
262
291
  }
263
292
  data.result = tracked.str;
264
293
  const { props } = tracked;
294
+
295
+ const customSources = [];
296
+
297
+ Object.keys(data.metadata.sourcesMap).forEach((k) => {
298
+ const objData = trackedObjects.get(data.metadata.sourcesMap[k]);
299
+
300
+ const { type, path, stackOpts } = objData;
301
+
302
+ const obj = data.metadata.sourcesMap[type];
303
+
304
+ customSources.push(
305
+ new SourceEvent({
306
+ context: new CallContext({
307
+ obj,
308
+ args: data.args,
309
+ result: data.result,
310
+ stackOpts
311
+ }),
312
+ signature: new Signature({
313
+ moduleName: 'http.req',
314
+ methodName: 'on.end',
315
+ args: undefined,
316
+ return: undefined,
317
+ isModule: false
318
+ }),
319
+ tagRanges,
320
+ source: 'A',
321
+ target: 'R',
322
+ name: path,
323
+ type,
324
+ isRequestObject: true
325
+ })
326
+ );
327
+ });
328
+
265
329
  props.tagRanges = tagRanges;
266
330
  // restore the original arguments so reporting shows user's signature.
267
331
  data.args = metadata.origArgs;
@@ -270,8 +334,12 @@ module.exports.handle = function() {
270
334
  // always exist, even if an empty array, for production code.
271
335
  if (metadata.sourceEvents && !metadata.sourceEvents.length) {
272
336
  // eslint-disable-next-line prettier/prettier
273
- const { sourceEvents, braned: { membrane } } = data.metadata;
274
- sourceEvents.push(membrane.makeReqSourceEvent(data.result.length));
337
+ const { sourceEvents, braned } = data.metadata;
338
+ if (braned) {
339
+ sourceEvents.push(
340
+ braned.membrane.makeReqSourceEvent(data.result.length)
341
+ );
342
+ }
275
343
  }
276
344
 
277
345
  props.event = new PropagationEvent({
@@ -280,7 +348,9 @@ module.exports.handle = function() {
280
348
  tagRanges: props.tagRanges,
281
349
  source: 'P',
282
350
  target: 'R',
283
- parents: data.metadata.sourceEvents
351
+ parents: customSources.length
352
+ ? customSources
353
+ : data.metadata.sourceEvents
284
354
  });
285
355
 
286
356
  return data.result;
@@ -0,0 +1,48 @@
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 requireHook = require('../../../hooks/require');
16
+ const patcher = require('../../../hooks/patcher');
17
+ const {
18
+ PATCH_TYPES: { ASSESS_PROPAGATOR }
19
+ } = require('../../../constants');
20
+ const tracker = require('../../../tracker');
21
+ const agent = require('../../../agent');
22
+
23
+ requireHook.resolve(
24
+ { name: 'joi', file: 'lib/types/any.js', version: '>=17.0.0' },
25
+ (object) => {
26
+ if (
27
+ !agent.config ||
28
+ (agent.config && !agent.config.agent.trust_custom_validators)
29
+ ) {
30
+ return;
31
+ }
32
+
33
+ patcher.patch(object._definition.rules.custom, 'validate', {
34
+ name: 'joi.any.custom.validate',
35
+ patchType: ASSESS_PROPAGATOR,
36
+ alwaysRun: true,
37
+ post(data) {
38
+ if (data.result && typeof data.result === 'string') {
39
+ tracker.untrack(data.result);
40
+ }
41
+
42
+ if (data.args[0] && typeof data.args[0] === 'string') {
43
+ tracker.untrack(data.args[0]);
44
+ }
45
+ }
46
+ });
47
+ }
48
+ );
@@ -21,4 +21,6 @@ module.exports.handle = function() {
21
21
  require('./string-schema');
22
22
  require('./string-base');
23
23
  require('./expression');
24
+ require('./object');
25
+ require('./any');
24
26
  };
@@ -0,0 +1,61 @@
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 requireHook = require('../../../hooks/require');
16
+ const patcher = require('../../../hooks/patcher');
17
+ const {
18
+ PATCH_TYPES: { ASSESS_PROPAGATOR }
19
+ } = require('../../../constants');
20
+ const tracker = require('../../../tracker');
21
+ const agent = require('../../../agent');
22
+
23
+ const traverseObjectAndUntrack = (object, incomingInput, result) => {
24
+ if (object.type === 'object' && object.$_terms.keys) {
25
+ object.$_terms.keys.forEach(({ key, schema }) => {
26
+ traverseObjectAndUntrack(schema, incomingInput[key], result[key]);
27
+ });
28
+ }
29
+
30
+ if (
31
+ object.type === 'string' &&
32
+ Array.isArray(object.$_terms.externals) &&
33
+ object.$_terms.externals.length
34
+ ) {
35
+ tracker.untrack(incomingInput);
36
+ tracker.untrack(result);
37
+ }
38
+ };
39
+
40
+ requireHook.resolve(
41
+ { name: 'joi', file: 'lib/types/object.js', version: '>=17.0.0' },
42
+ (object) => {
43
+ if (
44
+ !agent.config ||
45
+ (agent.config && !agent.config.agent.trust_custom_validators)
46
+ ) {
47
+ return;
48
+ }
49
+
50
+ patcher.patch(object.__proto__, 'validateAsync', {
51
+ name: 'joi.object.validateAsync',
52
+ patchType: ASSESS_PROPAGATOR,
53
+ alwaysRun: true,
54
+ post(data) {
55
+ data.result.then((result) =>
56
+ traverseObjectAndUntrack(data.obj, data.args[0], result)
57
+ );
58
+ }
59
+ });
60
+ }
61
+ );
@@ -24,6 +24,7 @@ const tracker = require('../../../tracker');
24
24
  const { PropagationEvent, Signature, CallContext } = require('../../models');
25
25
  const TagRange = require('../../models/tag-range');
26
26
  const tagRangeUtil = require('../../models/tag-range/util');
27
+ const agent = require('../../../agent');
27
28
 
28
29
  /**
29
30
  * Patch joi.string.validate so that it tags input with string-type-checked if validated
@@ -54,6 +55,21 @@ function instrumentJoiString(string) {
54
55
  }
55
56
  }
56
57
  });
58
+
59
+ if (agent.config.agent.trust_custom_validators) {
60
+ patcher.patch(string.__proto__, 'validateAsync', {
61
+ name: 'joi.string.validateAsync',
62
+ patchType: ASSESS_PROPAGATOR,
63
+ alwaysRun: true,
64
+ post(data) {
65
+ if (!data.obj.$_terms.externals.length) return;
66
+ data.result.then((result) => {
67
+ tracker.untrack(data.args[0]);
68
+ tracker.untrack(result);
69
+ });
70
+ }
71
+ });
72
+ }
57
73
  }
58
74
 
59
75
  requireHook.resolve(
@@ -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
  );