@contrast/agent 4.7.0 → 4.7.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 (76) 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/lib/assess/membrane/deserialization-membrane.js +4 -5
  6. package/lib/assess/membrane/source-membrane.js +15 -19
  7. package/lib/assess/models/call-context.js +1 -1
  8. package/lib/assess/policy/propagators.json +8 -0
  9. package/lib/assess/policy/rules.json +2 -2
  10. package/lib/assess/policy/signatures.json +27 -0
  11. package/lib/assess/policy/util.js +2 -1
  12. package/lib/assess/propagators/JSON/parse.js +1 -1
  13. package/lib/assess/propagators/JSON/stringify.js +3 -3
  14. package/lib/assess/propagators/array-prototype-join.js +7 -8
  15. package/lib/assess/propagators/common.js +7 -5
  16. package/lib/assess/propagators/handlebars-escape-expresssion.js +1 -1
  17. package/lib/assess/propagators/joi/boolean.js +1 -1
  18. package/lib/assess/propagators/joi/expression.js +1 -1
  19. package/lib/assess/propagators/joi/number.js +1 -1
  20. package/lib/assess/propagators/joi/string-base.js +1 -1
  21. package/lib/assess/propagators/joi/string-schema.js +12 -13
  22. package/lib/assess/propagators/joi/values.js +11 -11
  23. package/lib/assess/propagators/manager.js +12 -10
  24. package/lib/assess/propagators/mongoose/helpers.js +20 -0
  25. package/lib/assess/propagators/mongoose/index.js +18 -0
  26. package/lib/assess/propagators/mongoose/map.js +74 -0
  27. package/lib/assess/propagators/mongoose/string.js +104 -0
  28. package/lib/assess/propagators/number.js +54 -0
  29. package/lib/assess/propagators/object.js +6 -7
  30. package/lib/assess/propagators/path/basename.js +14 -13
  31. package/lib/assess/propagators/path/common.js +1 -1
  32. package/lib/assess/propagators/path/dirname.js +14 -13
  33. package/lib/assess/propagators/path/extname.js +14 -13
  34. package/lib/assess/propagators/path/parse.js +1 -1
  35. package/lib/assess/propagators/path/relative.js +7 -5
  36. package/lib/assess/propagators/querystring/escape.js +20 -18
  37. package/lib/assess/propagators/querystring/parse.js +7 -5
  38. package/lib/assess/propagators/querystring/stringify.js +25 -24
  39. package/lib/assess/propagators/querystring/unescape.js +20 -18
  40. package/lib/assess/propagators/sequelize/sql-string-escape.js +1 -1
  41. package/lib/assess/propagators/sequelize/sql-string-format-named-parameters.js +1 -1
  42. package/lib/assess/propagators/sequelize/sql-string-format.js +3 -3
  43. package/lib/assess/propagators/sequelize/utils.js +2 -2
  44. package/lib/assess/propagators/string-prototype-replace.js +30 -28
  45. package/lib/assess/propagators/string-prototype-split.js +36 -36
  46. package/lib/assess/propagators/string-prototype-trim.js +15 -17
  47. package/lib/assess/propagators/string.js +12 -16
  48. package/lib/assess/propagators/template-escape.js +21 -18
  49. package/lib/assess/propagators/templates.js +8 -8
  50. package/lib/assess/propagators/url/url-prototype-parse.js +5 -6
  51. package/lib/assess/propagators/url/url-url.js +51 -43
  52. package/lib/assess/propagators/util/format.js +1 -1
  53. package/lib/assess/propagators/v8/init-hooks.js +3 -3
  54. package/lib/assess/propagators/validator/init-hooks.js +22 -22
  55. package/lib/assess/sinks/common.js +10 -5
  56. package/lib/assess/sinks/libxmljs-xxe.js +1 -1
  57. package/lib/assess/sinks/mongodb.js +2 -1
  58. package/lib/assess/sinks/ssrf-url.js +1 -1
  59. package/lib/constants.js +4 -1
  60. package/lib/core/config/options.js +1 -1
  61. package/lib/core/rewrite/injections.js +8 -0
  62. package/lib/feature-set.js +1 -1
  63. package/lib/hooks/object-to-primitive.js +6 -7
  64. package/lib/hooks/patcher.js +1 -1
  65. package/lib/protect/rules/nosqli/nosql-injection-rule.js +228 -0
  66. package/lib/protect/rules/rule-factory.js +2 -2
  67. package/lib/protect/service.js +23 -11
  68. package/lib/protect/sinks/mongodb.js +56 -55
  69. package/lib/reporter/translations/to-protobuf/dtm/index.js +1 -1
  70. package/lib/reporter/translations/to-protobuf/dtm/ip-denylist-details.js +1 -1
  71. package/lib/reporter/translations/to-protobuf/dtm/rasp-rule-sample.js +1 -1
  72. package/lib/reporter/translations/to-protobuf/settings/defend-features.js +8 -6
  73. package/lib/reporter/translations/to-protobuf/settings/exclusions.js +5 -4
  74. package/lib/tracker.js +13 -65
  75. package/package.json +2 -1
  76. package/lib/protect/rules/nosqli/no-sql-injection-rule.js +0 -109
@@ -63,7 +63,7 @@ module.exports.handle = function handle() {
63
63
  if (tracked) {
64
64
  // it was behind a membrane
65
65
  data.result[TRACKED] = tracked;
66
- } else if (tracker.getData2(data.args[0])) {
66
+ } else if (tracker.getData(data.args[0])) {
67
67
  // it was a tracked string
68
68
  data.result[TRACKED] = data.args[0];
69
69
  }
@@ -80,13 +80,13 @@ module.exports.handle = function handle() {
80
80
  return;
81
81
  }
82
82
 
83
- const sTracking = tracker.getData2(tracked);
83
+ const sTracking = tracker.getData(tracked);
84
84
  // if the argument was a tracked string then the result should
85
85
  // have the same tags. i don't know that the length of a string
86
86
  // can change as a result of deserialize(serialize()) but best
87
87
  // to be safe.
88
88
  if (sTracking) {
89
- const resultTracking = tracker.track2(data.result);
89
+ const resultTracking = tracker.track(data.result);
90
90
  if (!resultTracking) {
91
91
  // there's nothing to do if tracking failed on the result. it should
92
92
  // only do so on a zero-length string, but node works in mysterious ways.
@@ -90,7 +90,7 @@ module.exports.handle = function() {
90
90
  function post(data) {
91
91
  if (data.result) {
92
92
  const trackingData = tracker.getData(data.args[0]);
93
- if (trackingData.tracked) {
93
+ if (trackingData) {
94
94
  tagRangeUtil.addInPlace(
95
95
  trackingData.tagRanges,
96
96
  new TagRange(0, data.args[0].length - 1, validators[validator])
@@ -131,7 +131,7 @@ module.exports.handle = function() {
131
131
  function post(data) {
132
132
  if (data.result) {
133
133
  const trackingData = tracker.getData(data.args[0]);
134
- if (trackingData.tracked) {
134
+ if (trackingData) {
135
135
  trackingData.tagRanges = [];
136
136
  trackingData.tracked = false;
137
137
  }
@@ -154,36 +154,36 @@ module.exports.handle = function() {
154
154
  function post(data) {
155
155
  // if not tracked then it can't be untrusted, so don't start
156
156
  // tracking this string.
157
- if (!tracker.getData(data.args[0]).tracked) return;
157
+ const inputData = tracker.getData(data.args[0]);
158
+ if (!inputData) return;
158
159
 
159
160
  const tag = sanitizers[sanitizer];
160
- // get existing tracker data
161
- const inputData = tracker.getData(data.args[0]);
162
161
 
163
162
  // are the input and output of the sanitizer the same?
164
163
  if (data.args[0] !== data.result) {
165
164
  let needsTag = true;
166
165
  // the input and output strings are not the same. start tracking the output.
167
- const result = tracker.track(data.result);
168
- const resultData = tracker.getData(result);
166
+ const tracked = tracker.track(data.result);
169
167
  // copy the input tags to the result, adjusting the range.
170
- const stop = data.result.length - 1;
171
- for (let i = 0; i < inputData.tagRanges.length; i++) {
172
- resultData.tagRanges[i] = new TagRange(
173
- 0,
174
- stop,
175
- inputData.tagRanges[i].tag
176
- );
177
- if (inputData.tagRanges[i].tag === tag) {
178
- needsTag = false;
168
+ if (tracked) {
169
+ const stop = data.result.length - 1;
170
+ for (let i = 0; i < inputData.tagRanges.length; i++) {
171
+ tracked.props.tagRanges[i] = new TagRange(
172
+ 0,
173
+ stop,
174
+ inputData.tagRanges[i].tag
175
+ );
176
+ if (inputData.tagRanges[i].tag === tag) {
177
+ needsTag = false;
178
+ }
179
179
  }
180
+ if (needsTag) {
181
+ tracked.props.tagRanges.push(
182
+ new TagRange(0, stop, sanitizers[sanitizer])
183
+ );
184
+ }
185
+ data.result = tracked.str;
180
186
  }
181
- if (needsTag) {
182
- resultData.tagRanges.push(
183
- new TagRange(0, stop, sanitizers[sanitizer])
184
- );
185
- }
186
- data.result = result;
187
187
  } else {
188
188
  // the input and output strings are the same, so the output is already being
189
189
  // tracked. if the tag is already present adjust the bounds otherwise add the
@@ -78,7 +78,9 @@ module.exports = (agent) => {
78
78
  // get the tags, event from dataflow actions ONLY
79
79
  if (sinkType === DATAFLOW) {
80
80
  const contrastProps = tracker.getData(input);
81
- ({ tagRanges, event } = contrastProps);
81
+ if (contrastProps) {
82
+ ({ tagRanges, event } = contrastProps);
83
+ }
82
84
  }
83
85
 
84
86
  const sink = new SinkEvent({
@@ -189,7 +191,7 @@ module.exports = (agent) => {
189
191
  ) {
190
192
  const trackData = tracker.getData(value);
191
193
 
192
- if (!trackData.tracked) {
194
+ if (!trackData) {
193
195
  return;
194
196
  }
195
197
 
@@ -415,9 +417,12 @@ module.exports = (agent) => {
415
417
  : common.nonDataflowVulnerableCheck({ sink });
416
418
 
417
419
  const checkedArgs = isVulnCheck(stack, result, args);
418
- return Array.isArray(checkedArgs)
419
- ? reduceConditions(checkedArgs, conditions.mode)
420
- : checkedArgs;
420
+ if (Array.isArray(checkedArgs) && checkedArgs.length > 0) {
421
+ const mode = conditions ? conditions.mode : 'and';
422
+ return reduceConditions(checkedArgs, mode);
423
+ } else {
424
+ return checkedArgs;
425
+ }
421
426
  };
422
427
 
423
428
  /**
@@ -34,7 +34,7 @@ module.exports = ({ common, signature }) => ({
34
34
  }
35
35
 
36
36
  const contrastProps = tracker.getData(xmlString);
37
- if (!contrastProps.tracked) {
37
+ if (!contrastProps) {
38
38
  return;
39
39
  }
40
40
 
@@ -39,7 +39,8 @@ const ruleId = 'nosql-injection';
39
39
  const disallowedTags = [
40
40
  'limited-chars',
41
41
  'alphanum-space-hyphen',
42
- 'string-type-checked'
42
+ 'string-type-checked',
43
+ 'custom-validated-nosql-injection'
43
44
  ];
44
45
  const requiredTags = ['untrusted'];
45
46
 
@@ -64,7 +64,7 @@ module.exports = ({ common: { isVulnerable } }) => {
64
64
 
65
65
  const contrastProps = tracker.getData(input);
66
66
 
67
- if (!contrastProps.tracked) {
67
+ if (!contrastProps) {
68
68
  return false;
69
69
  }
70
70
 
package/lib/constants.js CHANGED
@@ -108,8 +108,11 @@ const RULES = {
108
108
  'cmd-injection-semantic-chained-commands',
109
109
  CMD_INJECTION_SEMANTIC_DANGEROUS_PATHS:
110
110
  'cmd-injection-semantic-dangerous-paths',
111
- IP_DENYLIST: 'ip-blacklist',
111
+ IP_DENYLIST: 'ip-denylist',
112
112
  METHOD_TAMPERING: 'method-tampering',
113
+ // The following is not a known rule in TS and is only used by SR when
114
+ // reporting certain analysis results. We convert to nosqli before reporting
115
+ NOSQL_EXPANSION: 'nosql-expansion',
113
116
  NOSQL_INJECTION: 'nosql-injection',
114
117
  PATH_TRAVERSAL: 'path-traversal',
115
118
  REFLECTED_XSS: 'reflected-xss',
@@ -284,7 +284,7 @@ const agent = [
284
284
  {
285
285
  name: 'agent.node.array_request_sampling.enable',
286
286
  arg: '[false]',
287
- default: true,
287
+ default: false,
288
288
  fn: castBoolean,
289
289
  desc: 'enable sampling of array members for dataflow'
290
290
  },
@@ -131,6 +131,14 @@ const injections = {
131
131
  name: 'String',
132
132
  patchType: PATCH_TYPES.INJECTION
133
133
  })
134
+ ),
135
+ Number: new Injection(
136
+ 'Number',
137
+ 'ContrastNumber',
138
+ patcher.patch(global.Number, {
139
+ name: 'Number',
140
+ patchType: PATCH_TYPES.INJECTION
141
+ })
134
142
  )
135
143
  };
136
144
 
@@ -212,7 +212,7 @@ class FeatureSet {
212
212
  */
213
213
  getIpDenylistPolicy() {
214
214
  const config = this.getProtectRuleConfig(RULES.IP_DENYLIST);
215
- const settings = _.get(this, 'serverFeatures.defend.ipBlacklistsList', []);
215
+ const settings = _.get(this, 'serverFeatures.defend.ipDenylistsList', []);
216
216
 
217
217
  return {
218
218
  id: RULES.IP_DENYLIST,
@@ -31,13 +31,12 @@ const tracker = require('../tracker');
31
31
  function wrapNativeMethod(origFn, str) {
32
32
  let s = Reflect.apply(origFn, str, []);
33
33
  const origProps = tracker.getData(str);
34
- if (origProps.tracked) {
35
- s = tracker.track(s);
36
-
37
- const contrastProps = tracker.getData(s);
38
- if (contrastProps.tracked) {
39
- contrastProps.tagRanges = origProps.tagRanges;
40
- contrastProps.event = origProps.event;
34
+ if (origProps) {
35
+ const tracked = tracker.track(s);
36
+ if (tracked) {
37
+ s = tracked.str;
38
+ tracked.props.tagRanges = origProps.tagRanges;
39
+ tracked.props.event = origProps.event;
41
40
  }
42
41
  }
43
42
  return s;
@@ -111,7 +111,7 @@ function runHooks(type, data, thisTarget, fnHooks) {
111
111
  * @return {boolean}
112
112
  */
113
113
  function argsTracked(args) {
114
- return some(args, (arg) => arg && tracker.getData(arg).tracked);
114
+ return some(args, (arg) => arg && tracker.getData(arg));
115
115
  }
116
116
 
117
117
  /**
@@ -0,0 +1,228 @@
1
+ /**
2
+ Copyright: 2021 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
+ /* eslint-disable complexity */
17
+ const _ = require('lodash');
18
+
19
+ const logger = require('../../../core/logger')('contrast:rules:protect');
20
+ const { INPUT_TYPES, SINK_TYPES } = require('../common');
21
+ const AsyncStorage = require('../../../core/async-storage');
22
+ const constants = require('../../../constants');
23
+ const { traverse } = require('../../../util/traverse');
24
+
25
+ const brackets = /\[(.{1,1024}?)\]/;
26
+ const child = /\[(.{1,1024}?)\]/g;
27
+
28
+ const MONGODB = 'mongodb';
29
+
30
+ const ScannerKit = new Map([
31
+ [MONGODB, () => require('../nosqli/nosql-scanner').create('MongoDB')]
32
+ ]);
33
+ const SubstringFinder = require('../base-scanner/substring-finder');
34
+
35
+ class NoSqlInjectionRule extends require('../') {
36
+ constructor(policy = {}) {
37
+ policy.inputParseDepth = 3;
38
+ super(policy);
39
+
40
+ this._scanners = new Map();
41
+
42
+ this.id = 'nosql-injection';
43
+ this.name = 'NoSQL Injection';
44
+ this.applicableInputs = [
45
+ INPUT_TYPES.BODY,
46
+ INPUT_TYPES.JSON_VALUE,
47
+ INPUT_TYPES.JSON_ARRAYED_VALUE,
48
+ INPUT_TYPES.PARAMETER_NAME,
49
+ INPUT_TYPES.PARAMETER_VALUE,
50
+ INPUT_TYPES.QUERYSTRING,
51
+ INPUT_TYPES.XML_VALUE,
52
+ INPUT_TYPES.URI,
53
+ INPUT_TYPES.URL_PARAMETER
54
+ ];
55
+ this.applicableSinks = [SINK_TYPES.NOSQL_QUERY];
56
+ }
57
+
58
+ evaluateAtSink({ event, applicableSamples }) {
59
+ if (_.isEmpty(applicableSamples) || !event.data) {
60
+ return;
61
+ }
62
+
63
+ if (typeof event.data === 'object') {
64
+ for (const sample of applicableSamples) {
65
+ const requestData = this.getRequestData(sample.input);
66
+ if (!requestData) {
67
+ return;
68
+ }
69
+ let found;
70
+ traverse(event.data, (key, value) => {
71
+ if (found) {
72
+ return;
73
+ }
74
+
75
+ Object.keys(requestData).some((reqKey) => {
76
+ if (value[reqKey] === requestData[reqKey]) {
77
+ found = reqKey;
78
+ return true;
79
+ }
80
+ });
81
+ });
82
+
83
+ if (found) {
84
+ const query = require('util').inspect(event.data, false, null);
85
+
86
+ let injection;
87
+ for (const location of new SubstringFinder(query, found)) {
88
+ if (location) {
89
+ injection = {
90
+ input: found,
91
+ location,
92
+ query
93
+ };
94
+ break;
95
+ }
96
+ }
97
+ if (injection) {
98
+ this.appendAttackDetails(sample, injection);
99
+ sample.captureAppContext(event);
100
+ logger.warn(`EFFECTIVE - rule: ${this.id}, mode: ${this.mode}`);
101
+ this.blockRequest(sample);
102
+ }
103
+ }
104
+ }
105
+ } else if (typeof event.data === 'string' || event.data instanceof String) {
106
+ const scanner = this.getScanner(event.id);
107
+
108
+ for (const sample of applicableSamples) {
109
+ const injection = scanner.findInjection(sample.input.value, event.data);
110
+
111
+ if (injection) {
112
+ this.appendAttackDetails(sample, injection);
113
+ sample.captureAppContext(event);
114
+ logger.warn(`EFFECTIVE - rule: ${this.id}, mode: ${this.mode} `);
115
+ this.blockRequest(sample);
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Given the sample's user input object, will read the value from the request
123
+ * from the async storage context.
124
+ * @param {string} _documentPath
125
+ * @param {string} _documentType
126
+ */
127
+ getRequestData({ _documentPath, _documentType }) {
128
+ const ctx = AsyncStorage.getContext();
129
+
130
+ if (!ctx) {
131
+ logger.info(
132
+ 'unable to perform nosql-expansion sink analysis: async storage context not available'
133
+ );
134
+ return;
135
+ }
136
+
137
+ const pathArray = [];
138
+
139
+ if (!_documentPath) {
140
+ return pathArray;
141
+ }
142
+
143
+ if (
144
+ _documentType === constants.INPUT_TYPES.PARAMETER_NAME &&
145
+ _documentPath.length <= 1024
146
+ ) {
147
+ let segment = brackets.exec(_documentPath);
148
+ const parent = segment
149
+ ? _documentPath.slice(0, segment.index)
150
+ : _documentPath;
151
+
152
+ pathArray.push('query');
153
+
154
+ if (parent) {
155
+ pathArray.push(parent);
156
+ }
157
+ while ((segment = child.exec(_documentPath))) {
158
+ pathArray.push(segment[1]);
159
+ }
160
+ pathArray.pop();
161
+ } else {
162
+ // only qs params and body are "expandable"
163
+ pathArray.push('body', ..._documentPath.split('.'));
164
+ }
165
+
166
+ let data;
167
+ let curr = ctx.req;
168
+
169
+ for (const path of pathArray) {
170
+ if (curr) {
171
+ data = curr[path];
172
+ curr = data;
173
+ }
174
+ }
175
+ return data;
176
+ }
177
+
178
+ getScanner(id) {
179
+ if (!ScannerKit.has(id)) {
180
+ throw new Error(`Unknown NoSQL scanner: ${id}`);
181
+ }
182
+
183
+ if (!this._scanners.has(id)) {
184
+ this._scanners.set(id, ScannerKit.get(id)());
185
+ }
186
+
187
+ return this._scanners.get(id);
188
+ }
189
+
190
+ /**
191
+ * Builds details for NoSQL Injection Attack.
192
+ * @param {UserInput} inputDtm The user input that resulted in attack
193
+ * @param {String} query The query that was analyzed
194
+ * @param {Object} results The repsults of the sql-scanner
195
+ * @returns {Object} The details
196
+ */
197
+ buildDetails(sample, findings) {
198
+ if (!findings) {
199
+ return null;
200
+ }
201
+
202
+ const { boundary, location, query } = findings;
203
+ let inputBoundaryIndex, boundaryOverrunIndex;
204
+ const start = location[0];
205
+ const end = location[1] + 1;
206
+
207
+ if (boundary) {
208
+ inputBoundaryIndex = boundary.previous
209
+ ? boundary.previous.start
210
+ : boundary.start;
211
+ boundaryOverrunIndex = boundary.stop + 1;
212
+ } else {
213
+ inputBoundaryIndex = start;
214
+ boundaryOverrunIndex = end;
215
+ }
216
+
217
+ return {
218
+ start,
219
+ end,
220
+ input: sample.input.toSerializable(),
221
+ boundaryOverrunIndex,
222
+ inputBoundaryIndex,
223
+ query
224
+ };
225
+ }
226
+ }
227
+
228
+ module.exports = NoSqlInjectionRule;
@@ -34,7 +34,7 @@ const ctors = {
34
34
  [RULES.CMD_INJECTION_SEMANTIC_CHAINED_COMMANDS]: require('./cmd-injection-semantic-chained-commands/cmd-injection-semantic-chained-commands-rule'),
35
35
  [RULES.CMD_INJECTION_SEMANTIC_DANGEROUS_PATHS]: require('./cmd-injection-semantic-dangerous-paths/cmd-injection-semantic-dangerous-paths-rule.js'),
36
36
  [RULES.METHOD_TAMPERING]: require('./method-tampering/method-tampering-rule'),
37
- [RULES.NOSQL_INJECTION]: require('./nosqli/no-sql-injection-rule'),
37
+ [RULES.NOSQL_INJECTION]: require('./nosqli/nosql-injection-rule'),
38
38
  [RULES.PATH_TRAVERSAL]: require('./path-traversal/path-traversal-rule'),
39
39
  [RULES.REFLECTED_XSS]: require('./xss/reflected-xss-rule'),
40
40
  [RULES.SQL_INJECTION]: require('./sqli/sql-injection-rule'),
@@ -109,7 +109,7 @@ class ProtectRuleFactory {
109
109
  this.signatures = new SignatureKit(definitionList);
110
110
  }
111
111
 
112
- const denylist = _.get(settings, 'defend.ipBlacklistsList');
112
+ const denylist = _.get(settings, 'defend.ipDenylistsList');
113
113
  if (denylist) {
114
114
  logger.info(`IP Denylist updated. Total: ${denylist.length}`);
115
115
  }
@@ -20,7 +20,12 @@ Copyright: 2021 Contrast Security, Inc
20
20
 
21
21
  const _ = require('lodash');
22
22
 
23
- const { IMPORTANCE, SAFE_HEADER_VALUES, INPUT_TYPES } = require('../constants');
23
+ const {
24
+ IMPORTANCE,
25
+ SAFE_HEADER_VALUES,
26
+ INPUT_TYPES,
27
+ RULES
28
+ } = require('../constants');
24
29
  const agentEmitter = require('../agent-emitter');
25
30
  const SampleAggregator = require('./sample-aggregator');
26
31
  const RuleFactory = require('./rules/rule-factory');
@@ -203,15 +208,22 @@ class ProtectService {
203
208
  appContext.request = request;
204
209
  }
205
210
 
206
- for (const {
207
- scoreLevel,
208
- ruleId,
209
- inputType: type,
210
- path,
211
- key: name,
212
- value,
213
- idsList
214
- } of resultsList) {
211
+ for (const result of resultsList) {
212
+ // Coerce custom rule id
213
+ if (result.ruleId === RULES.NOSQL_EXPANSION) {
214
+ result.ruleId = RULES.NOSQL_INJECTION;
215
+ }
216
+
217
+ const {
218
+ scoreLevel,
219
+ ruleId,
220
+ inputType: type,
221
+ path,
222
+ key: name,
223
+ value,
224
+ idsList
225
+ } = result;
226
+
215
227
  if (scoreLevel === IMPORTANCE.NONE) {
216
228
  return;
217
229
  }
@@ -269,7 +281,7 @@ class ProtectService {
269
281
  * Loads IP analyzer for allowist analysis given TS settings.
270
282
  */
271
283
  updateIpAllowlist(settings) {
272
- const list = _.get(settings, 'defend.ipWhitelistsList');
284
+ const list = _.get(settings, 'defend.ipAllowlistsList');
273
285
  if (list && list.length) {
274
286
  this.ipAllowlist = new IpAnalyzer(list);
275
287
  this.ipAllowlist.on('expired', (dtm) => {