@contrast/assess 1.10.0 → 1.11.0

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 (116) hide show
  1. package/lib/dataflow/event-factory.js +1 -1
  2. package/lib/dataflow/index.js +1 -1
  3. package/lib/dataflow/propagation/common.js +1 -1
  4. package/lib/dataflow/propagation/index.js +2 -1
  5. package/lib/dataflow/propagation/install/JSON/index.js +1 -1
  6. package/lib/dataflow/propagation/install/JSON/parse-fn.js +1 -1
  7. package/lib/dataflow/propagation/install/JSON/parse.js +1 -1
  8. package/lib/dataflow/propagation/install/JSON/stringify.js +1 -1
  9. package/lib/dataflow/propagation/install/array-prototype-join.js +1 -1
  10. package/lib/dataflow/propagation/install/buffer.js +1 -1
  11. package/lib/dataflow/propagation/install/contrast-methods/add.js +1 -1
  12. package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -1
  13. package/lib/dataflow/propagation/install/contrast-methods/number.js +1 -1
  14. package/lib/dataflow/propagation/install/contrast-methods/string.js +1 -1
  15. package/lib/dataflow/propagation/install/contrast-methods/tag.js +1 -1
  16. package/lib/dataflow/propagation/install/decode-uri-component.js +1 -1
  17. package/lib/dataflow/propagation/install/ejs/escape-xml.js +1 -1
  18. package/lib/dataflow/propagation/install/ejs/index.js +1 -1
  19. package/lib/dataflow/propagation/install/encode-uri-component.js +1 -1
  20. package/lib/dataflow/propagation/install/escape-html.js +1 -1
  21. package/lib/dataflow/propagation/install/escape.js +1 -1
  22. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +1 -1
  23. package/lib/dataflow/propagation/install/isnumeric-0.js +1 -1
  24. package/lib/dataflow/propagation/install/mongoose/common.js +20 -0
  25. package/lib/dataflow/propagation/install/mongoose/index.js +5 -9
  26. package/lib/dataflow/propagation/install/mongoose/schema-map.js +149 -0
  27. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +162 -0
  28. package/lib/dataflow/propagation/install/mongoose/schema-string.js +91 -37
  29. package/lib/dataflow/propagation/install/mysql-connection-escape.js +1 -1
  30. package/lib/dataflow/propagation/install/parse-int.js +1 -1
  31. package/lib/dataflow/propagation/install/path/basename.js +1 -1
  32. package/lib/dataflow/propagation/install/path/common.js +1 -1
  33. package/lib/dataflow/propagation/install/path/index.js +1 -1
  34. package/lib/dataflow/propagation/install/path/join-and-resolve.js +1 -1
  35. package/lib/dataflow/propagation/install/path/normalize.js +1 -1
  36. package/lib/dataflow/propagation/install/pug/index.js +1 -1
  37. package/lib/dataflow/propagation/install/pug-runtime-escape.js +1 -1
  38. package/lib/dataflow/propagation/install/querystring/index.js +1 -1
  39. package/lib/dataflow/propagation/install/querystring/parse.js +1 -1
  40. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +182 -0
  41. package/lib/dataflow/propagation/install/sequelize.js +1 -1
  42. package/lib/dataflow/propagation/install/sql-template-strings.js +1 -1
  43. package/lib/dataflow/propagation/install/string/concat.js +1 -1
  44. package/lib/dataflow/propagation/install/string/format-methods.js +1 -1
  45. package/lib/dataflow/propagation/install/string/html-methods.js +1 -1
  46. package/lib/dataflow/propagation/install/string/index.js +65 -1
  47. package/lib/dataflow/propagation/install/string/match-all.js +236 -0
  48. package/lib/dataflow/propagation/install/string/match.js +83 -37
  49. package/lib/dataflow/propagation/install/string/replace.js +2 -2
  50. package/lib/dataflow/propagation/install/string/slice.js +1 -1
  51. package/lib/dataflow/propagation/install/string/split.js +1 -1
  52. package/lib/dataflow/propagation/install/string/substring.js +1 -1
  53. package/lib/dataflow/propagation/install/string/trim.js +1 -1
  54. package/lib/dataflow/propagation/install/unescape.js +1 -1
  55. package/lib/dataflow/propagation/install/url/domain-parsers.js +1 -1
  56. package/lib/dataflow/propagation/install/url/index.js +3 -1
  57. package/lib/dataflow/propagation/install/url/parse.js +131 -0
  58. package/lib/dataflow/propagation/install/url/searchParams.js +133 -0
  59. package/lib/dataflow/propagation/install/url/url.js +9 -52
  60. package/lib/dataflow/propagation/install/validator/hooks.js +1 -1
  61. package/lib/dataflow/propagation/install/validator/index.js +1 -1
  62. package/lib/dataflow/propagation/install/validator/methods.js +1 -1
  63. package/lib/dataflow/sinks/common.js +1 -1
  64. package/lib/dataflow/sinks/index.js +1 -1
  65. package/lib/dataflow/sinks/install/child-process.js +1 -1
  66. package/lib/dataflow/sinks/install/eval.js +1 -1
  67. package/lib/dataflow/sinks/install/express/index.js +1 -1
  68. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +1 -1
  69. package/lib/dataflow/sinks/install/fastify/index.js +1 -1
  70. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +1 -1
  71. package/lib/dataflow/sinks/install/fs.js +1 -1
  72. package/lib/dataflow/sinks/install/function.js +1 -1
  73. package/lib/dataflow/sinks/install/http/index.js +1 -1
  74. package/lib/dataflow/sinks/install/http/request.js +1 -1
  75. package/lib/dataflow/sinks/install/http/server-response.js +1 -1
  76. package/lib/dataflow/sinks/install/koa/index.js +1 -1
  77. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +1 -1
  78. package/lib/dataflow/sinks/install/marsdb.js +1 -1
  79. package/lib/dataflow/sinks/install/mongodb.js +31 -24
  80. package/lib/dataflow/sinks/install/mssql.js +1 -1
  81. package/lib/dataflow/sinks/install/mysql.js +1 -1
  82. package/lib/dataflow/sinks/install/postgres.js +1 -1
  83. package/lib/dataflow/sinks/install/sequelize.js +1 -1
  84. package/lib/dataflow/sinks/install/sqlite3.js +1 -1
  85. package/lib/dataflow/sinks/install/vm.js +1 -1
  86. package/lib/dataflow/sources/common.js +1 -1
  87. package/lib/dataflow/sources/handler.js +1 -1
  88. package/lib/dataflow/sources/index.js +1 -1
  89. package/lib/dataflow/sources/install/body-parser1.js +1 -1
  90. package/lib/dataflow/sources/install/busboy1.js +1 -1
  91. package/lib/dataflow/sources/install/cookie-parser1.js +1 -1
  92. package/lib/dataflow/sources/install/express/index.js +1 -1
  93. package/lib/dataflow/sources/install/express/params.js +1 -1
  94. package/lib/dataflow/sources/install/express/parsedUrl.js +1 -1
  95. package/lib/dataflow/sources/install/fastify/fastify.js +1 -1
  96. package/lib/dataflow/sources/install/fastify/index.js +1 -1
  97. package/lib/dataflow/sources/install/formidable1.js +1 -1
  98. package/lib/dataflow/sources/install/http.js +1 -1
  99. package/lib/dataflow/sources/install/koa/index.js +1 -1
  100. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +1 -1
  101. package/lib/dataflow/sources/install/koa/koa-routers.js +1 -1
  102. package/lib/dataflow/sources/install/koa/koa2.js +1 -1
  103. package/lib/dataflow/sources/install/qs6.js +1 -1
  104. package/lib/dataflow/sources/install/querystring.js +1 -1
  105. package/lib/dataflow/tag-utils.js +1 -1
  106. package/lib/dataflow/tracker.js +1 -1
  107. package/lib/dataflow/utils/is-safe-content-type.js +1 -1
  108. package/lib/dataflow/utils/is-vulnerable.js +1 -1
  109. package/lib/index.js +1 -1
  110. package/lib/response-scanning/handlers/index.js +36 -30
  111. package/lib/response-scanning/handlers/utils.js +1 -1
  112. package/lib/response-scanning/index.js +1 -1
  113. package/lib/response-scanning/install/http.js +3 -3
  114. package/lib/session-configuration/index.js +1 -1
  115. package/lib/session-configuration/install/http.js +1 -1
  116. package/package.json +2 -2
@@ -0,0 +1,133 @@
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
+
16
+ 'use strict';
17
+
18
+ const { patchType } = require('../../common');
19
+ const { inspect, isString } = require('@contrast/common');
20
+
21
+ module.exports = function(core) {
22
+ const {
23
+ scopes: { sources, instrumentation },
24
+ patcher,
25
+ depHooks,
26
+ assess: {
27
+ dataflow: { tracker, eventFactory: { createPropagationEvent } }
28
+ }
29
+ } = core;
30
+
31
+ function getPropagationEvent(params, paramInfo, data) {
32
+ return createPropagationEvent({
33
+ name: 'url.URLSearchParams',
34
+ moduleName: 'url',
35
+ methodName: 'URLSearchParams',
36
+ context: `url.URLSearchParams('${inspect(params)}')`,
37
+ object: {
38
+ value: 'url',
39
+ tracked: false
40
+ },
41
+ result: {
42
+ value: inspect(data.result),
43
+ tracked: true
44
+ },
45
+ args: [{ value: inspect(params), tracked: false }],
46
+ tags: paramInfo.tags,
47
+ history: [paramInfo],
48
+ source: 'P',
49
+ target: 'R',
50
+ stacktraceOpts: {
51
+ constructorOpt: data.hooked,
52
+ prependFrames: [data.orig]
53
+ },
54
+ });
55
+ }
56
+
57
+ return core.assess.dataflow.propagation.urlInstrumentation.searchParams = {
58
+ install() {
59
+ depHooks.resolve({ name: 'url' }, (url) => {
60
+
61
+ const name = 'url.URLSearchParams';
62
+
63
+ patcher.patch(url, 'URLSearchParams', {
64
+ name,
65
+ patchType,
66
+ post(data) {
67
+ const { args, obj, result } = data;
68
+ if (!result || !args[0] || !sources.getStore()?.assess || instrumentation.isLocked()) return;
69
+
70
+ const [params] = args;
71
+
72
+ if (isString(params)) {
73
+ params.split('&').forEach((query) => {
74
+ const startIdx = query.indexOf('?') + 1;
75
+ const endIdx = query.indexOf('=');
76
+ const key = query.substring(startIdx, endIdx);
77
+ const param = query.substring(endIdx + 1, query.length);
78
+ const paramInfo = tracker.getData(param);
79
+ if (!paramInfo) return;
80
+
81
+ const event = getPropagationEvent(params, paramInfo, data);
82
+ if (!event);
83
+
84
+ Object.assign(paramInfo, event);
85
+ const { extern } = paramInfo || tracker.track(param, event);
86
+
87
+ if (extern) {
88
+ result.set(key, extern);
89
+ }
90
+ });
91
+ }
92
+
93
+ if (typeof params === 'object') {
94
+ Object.keys(params).forEach((key) => {
95
+ const paramInfo = tracker.getData(params[key]);
96
+ if (!paramInfo) return;
97
+
98
+ const event = getPropagationEvent(params, paramInfo, data);
99
+ if (!event) return;
100
+
101
+ Object.assign(paramInfo, event);
102
+ const { extern } = paramInfo || tracker.track(params[key], event);
103
+
104
+ if (extern) {
105
+ result.set(key, extern);
106
+ }
107
+ });
108
+ }
109
+
110
+ patcher.patch(obj.prototype, 'toString', {
111
+ name: 'url.URLSearchParams.toString',
112
+ patchType,
113
+ post(data) {
114
+ const { obj: params } = data;
115
+ let i = 0;
116
+ let str = '';
117
+ params.forEach((val, key) => {
118
+ if (i === 0) {
119
+ str = key.concat('=', val);
120
+ } else {
121
+ str = str.concat('&', key, '=', val);
122
+ }
123
+ i++;
124
+ });
125
+ data.result = str;
126
+ }
127
+ });
128
+ }
129
+ });
130
+ });
131
+ },
132
+ };
133
+ };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -42,7 +42,6 @@ module.exports = function(core) {
42
42
  ],
43
43
  'pathname',
44
44
  'search',
45
- 'searchParams',
46
45
  'hash'
47
46
  ]
48
47
  ];
@@ -84,6 +83,7 @@ module.exports = function(core) {
84
83
  return core.assess.dataflow.propagation.urlInstrumentation.url = {
85
84
  install() {
86
85
  depHooks.resolve({ name: 'url' }, (url) => {
86
+ const { URLSearchParams } = url;
87
87
  const name = 'url.URL';
88
88
  patcher.patch(url, 'URL', {
89
89
  name,
@@ -116,33 +116,11 @@ module.exports = function(core) {
116
116
  const strInfo = tracker.getData(url);
117
117
  if (!strInfo) return;
118
118
 
119
- const searchParamsProxy = new Proxy(data.result.searchParams, {
120
- set(obj, prop, value) {
121
- return Reflect.set(obj, prop, value);
122
- },
123
- get(obj, prop, receiver) {
124
- const val = obj[prop];
125
- if (val instanceof Function) {
126
- return function (query) {
127
- if (typeof query === 'string') {
128
- const trackedProp = Reflect.get(obj, `_contrast_${query}`);
129
- if (trackedProp) return trackedProp;
130
- }
131
- return val.apply(this === receiver ? obj : this, query);
132
- };
133
- }
134
- return Reflect.get(obj, prop, receiver);
135
- }
136
- });
137
-
138
119
  const proxy = new Proxy(data.result, {
139
120
  set(obj, prop, value) {
140
121
  return Reflect.set(obj, prop, value);
141
122
  },
142
123
  get(obj, prop) {
143
- if (prop === 'searchParams') {
144
- return searchParamsProxy;
145
- }
146
124
  if (prop === 'toString' || prop === 'toJSON') {
147
125
  return function() {
148
126
  return Reflect.get(obj, '_contrast_href');
@@ -159,7 +137,7 @@ module.exports = function(core) {
159
137
  const traverse = function(href, url, keys, idx = 0) {
160
138
  let substr = href;
161
139
  keys.forEach((key) => {
162
- if (typeof key === 'string' && key !== 'searchParams') {
140
+ if (typeof key === 'string') {
163
141
  const part = url[key];
164
142
  if (part !== 'null') {
165
143
 
@@ -180,38 +158,13 @@ module.exports = function(core) {
180
158
  const event = getPropagationEvent(strInfo, partInfo, data);
181
159
  if (!event) return;
182
160
 
183
- if (partInfo) {
184
- Object.assign(partInfo, event);
185
- }
186
- const { extern } = partInfo || tracker.track(part, event);
161
+ Object.assign(partInfo, event);
162
+ const { extern } = tracker.track(part, event);
187
163
 
188
164
  if (extern) {
189
165
  proxy[`_contrast_${key}`] = extern;
190
166
  }
191
167
  }
192
- } else if (key === 'searchParams') {
193
- const queries = proxy.search.split('&');
194
- queries.forEach((query) => {
195
- const startIdx = query.indexOf('?') + 1;
196
- const endIdx = query.indexOf('=');
197
- const queryName = query.substring(startIdx, endIdx);
198
- const queryString = query.substring(endIdx + 1, query.length);
199
- const queryStringInfo = tracker.getData(queryString);
200
- if (!queryStringInfo) return;
201
-
202
- const event = getPropagationEvent(strInfo, queryStringInfo, data);
203
-
204
- if (!event) return;
205
-
206
- if (queryStringInfo) {
207
- Object.assign(queryStringInfo, event);
208
- }
209
- const { extern } = queryStringInfo || tracker.track(queryString, event);
210
-
211
- if (extern) {
212
- searchParamsProxy[`_contrast_${queryName}`] = extern;
213
- }
214
- });
215
168
  } else {
216
169
  traverse(substr, url, key, 0);
217
170
  }
@@ -219,6 +172,10 @@ module.exports = function(core) {
219
172
  };
220
173
 
221
174
  traverse(url, result, keys, 0);
175
+
176
+ if (proxy.search) {
177
+ proxy._contrast_searchParams = new URLSearchParams(proxy.search);
178
+ }
222
179
  data.result = proxy;
223
180
  }
224
181
  });
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -85,16 +85,16 @@ module.exports = function(core) {
85
85
  const instr = core.assess.dataflow.sinks.mongodb = {};
86
86
 
87
87
  instr.getQueryVulnerabilityInfo = function getQueryVulnerabilityInfo(query) {
88
+ const reportSafe = [];
88
89
  let vulnInfo = null;
89
- let reportSafe = null;
90
90
 
91
91
  if (isString(query)) {
92
92
  const strInfo = tracker.getData(query);
93
93
  if (strInfo) {
94
94
  if (isVulnerable(UNTRUSTED, querySafeTags, strInfo.tags)) {
95
95
  vulnInfo = { strInfo };
96
- } else {
97
- reportSafe = { strInfo };
96
+ } else if (config.assess.safe_positives.enable) {
97
+ reportSafe.push({ strInfo });
98
98
  }
99
99
  }
100
100
 
@@ -109,8 +109,8 @@ module.exports = function(core) {
109
109
  if (isVulnerable(UNTRUSTED, querySafeTags, strInfo.tags)) {
110
110
  vulnInfo = { path: [...path], strInfo };
111
111
  return true; // halts traversal
112
- } else {
113
- reportSafe = { path: [...path], strInfo };
112
+ } else if (config.assess.safe_positives.enable) {
113
+ reportSafe.push({ path: [...path], strInfo });
114
114
  }
115
115
  }
116
116
  });
@@ -119,8 +119,8 @@ module.exports = function(core) {
119
119
  };
120
120
 
121
121
  instr.getAggregateVulnerabilityInfo = function getAggregateVulnerabilityInfo(aggregation) {
122
+ const reportSafe = [];
122
123
  let vulnInfo = null;
123
- let reportSafe = null;
124
124
 
125
125
  if (!isNonEmptyObject(aggregation)) return { vulnInfo, reportSafe };
126
126
 
@@ -141,8 +141,8 @@ module.exports = function(core) {
141
141
  if (isVulnerable(UNTRUSTED, querySafeTags, strInfo.tags)) {
142
142
  vulnInfo = { path: [...path], strInfo };
143
143
  return true; // halts traversal
144
- } else {
145
- reportSafe = { path: [...path], strInfo };
144
+ } else if (config.assess.safe_positives.enable) {
145
+ reportSafe.push({ path: [...path], strInfo });
146
146
  }
147
147
  }
148
148
  }
@@ -152,16 +152,16 @@ module.exports = function(core) {
152
152
  };
153
153
 
154
154
  instr.getMapReduceVulnerabilityInfo = function getMapReduceVulnerabilityInfo(argToCheck, argIdx) {
155
+ const reportSafe = [];
155
156
  let vulnInfo = null;
156
- let reportSafe = null;
157
157
 
158
158
  if (argIdx !== 2 && isString(argToCheck)) {
159
159
  const strInfo = tracker.getData(argToCheck);
160
160
  if (strInfo) {
161
161
  if (isVulnerable(UNTRUSTED, querySafeTags, strInfo.tags)) {
162
162
  vulnInfo = { strInfo };
163
- } else {
164
- reportSafe = { strInfo };
163
+ } else if (config.assess.safe_positives.enable) {
164
+ reportSafe.push({ strInfo });
165
165
  }
166
166
  }
167
167
 
@@ -181,8 +181,8 @@ module.exports = function(core) {
181
181
  if (isVulnerable(UNTRUSTED, querySafeTags, strInfo.tags)) {
182
182
  vulnInfo = { path: [...path], strInfo };
183
183
  return true; // halts traversal
184
- } else {
185
- reportSafe = { path: [...path], strInfo };
184
+ } else if (config.assess.safe_positives.enable) {
185
+ reportSafe.push({ path: [...path], strInfo });
186
186
  }
187
187
  }
188
188
  }
@@ -192,16 +192,16 @@ module.exports = function(core) {
192
192
  };
193
193
 
194
194
  instr.getGroupVulnerabilityInfo = function getGroupVulnerabilityInfo(argToCheck, argIdx) {
195
+ const reportSafe = [];
195
196
  let vulnInfo = null;
196
- let reportSafe = null;
197
197
 
198
198
  if (argIdx !== 1 && isString(argToCheck)) {
199
199
  const strInfo = tracker.getData(argToCheck);
200
200
  if (strInfo) {
201
201
  if (isVulnerable(UNTRUSTED, querySafeTags, strInfo.tags)) {
202
202
  vulnInfo = { strInfo };
203
- } else {
204
- reportSafe = { strInfo };
203
+ } else if (config.assess.safe_positives.enable) {
204
+ reportSafe.push({ strInfo });
205
205
  }
206
206
  }
207
207
 
@@ -216,8 +216,8 @@ module.exports = function(core) {
216
216
  if (isVulnerable(UNTRUSTED, querySafeTags, strInfo.tags)) {
217
217
  vulnInfo = { path: [...path], strInfo };
218
218
  return true; // halts traversal
219
- } else {
220
- reportSafe = { path: [...path], strInfo };
219
+ } else if (config.assess.safe_positives.enable) {
220
+ reportSafe.push({ path: [...path], strInfo });
221
221
  }
222
222
  }
223
223
  });
@@ -248,12 +248,19 @@ module.exports = function(core) {
248
248
  vulnArgIdx = argIdx;
249
249
  break;
250
250
  }
251
- if (reportSafe) safeReports.push({ ...reportSafe, argIdx });
251
+
252
+ if (config.assess.safe_positives.enable && reportSafe.length) {
253
+ reportSafe.forEach(el => safeReports.push({ ...el, argIdx }));
254
+ }
252
255
  }
253
256
 
254
257
  if (!vulnInfo) {
255
258
  if (safeReports.length && config.assess.safe_positives.enable) {
256
- const safeTags = safeReports.map((report) => filterSafeTags(querySafeTags, report.strInfo));
259
+ const safeTags = safeReports
260
+ .map((report) => filterSafeTags(querySafeTags, report.strInfo))
261
+ .flat()
262
+ .filter((value, index, self) => index === self.indexOf(value));
263
+
257
264
  const strInfo = safeReports.map((report) => {
258
265
  const tags = report.path ? utils.createAdjustedQueryTags(report.path, report.strInfo.tags, report.strInfo.value, inspect(origArgs[report.argIdx], { depth: 4 })) : report.strInfo?.tags;
259
266
 
@@ -266,7 +273,7 @@ module.exports = function(core) {
266
273
  reportSafePositive({
267
274
  name,
268
275
  ruleId: NOSQL_INJECTION_MONGO,
269
- safeTags: safeTags.length === 1 ? safeTags[0] : safeTags,
276
+ safeTags,
270
277
  strInfo: strInfo.length === 1 ? strInfo[0] : strInfo
271
278
  });
272
279
  }
@@ -323,8 +330,6 @@ module.exports = function(core) {
323
330
  });
324
331
  };
325
332
 
326
- return instr;
327
-
328
333
  function patchCollection(mongodb, version) {
329
334
  for (const method of collectionMethods) {
330
335
  const proto = mongodb.Collection.prototype;
@@ -400,4 +405,6 @@ module.exports = function(core) {
400
405
 
401
406
  return name;
402
407
  }
408
+
409
+ return instr;
403
410
  };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5