@contrast/protect 1.7.0 → 1.8.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.
@@ -15,7 +15,13 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { BLOCKING_MODES, simpleTraverse, Rule, isString } = require('@contrast/common');
18
+ const {
19
+ BLOCKING_MODES,
20
+ simpleTraverse,
21
+ Rule,
22
+ isString,
23
+ ProtectRuleMode: { OFF },
24
+ } = require('@contrast/common');
19
25
  const address = require('ipaddr.js');
20
26
 
21
27
  //
@@ -57,6 +63,14 @@ module.exports = function(core) {
57
63
  config,
58
64
  } = core;
59
65
 
66
+ const jsonInputTypes = {
67
+ keyType: agentLib.InputType.JsonKey, inputType: agentLib.InputType.JsonValue
68
+ };
69
+
70
+ const parameterInputTypes = {
71
+ keyType: agentLib.InputType.ParameterKey, inputType: agentLib.InputType.ParameterValue
72
+ };
73
+
60
74
  // all handlers will be invoked with two arguments:
61
75
  // 1) sourceContext object containing:
62
76
  // - reqData, the abstract request object containing only what is needed
@@ -112,8 +126,6 @@ module.exports = function(core) {
112
126
  * @returns {undefined|[String]} undefined to permit else [mode, rule] to block.
113
127
  */
114
128
  inputAnalysis.handleConnect = function handleConnect(sourceContext, connectInputs) {
115
- if (!sourceContext || sourceContext.allowed) return;
116
-
117
129
  const { policy: { rulesMask } } = sourceContext;
118
130
 
119
131
  inputAnalysis.handleVirtualPatches(sourceContext, { URLS: connectInputs.rawUrl, HEADERS: connectInputs.headers });
@@ -122,84 +134,13 @@ module.exports = function(core) {
122
134
  let block = undefined;
123
135
  if (rulesMask !== 0) {
124
136
  const findings = agentLib.scoreRequestConnect(rulesMask, connectInputs, preferWW);
137
+
125
138
  block = mergeFindings(sourceContext, findings);
126
139
  }
127
140
 
128
141
  return block;
129
142
  };
130
143
 
131
- /**
132
- * handleRequestEnd()
133
- *
134
- * Invoked when the request is complete.
135
- *
136
- * @param {Object} sourceContext
137
- */
138
- inputAnalysis.handleRequestEnd = function handleRequestEnd(sourceContext) {
139
- if (!config.protect.probe_analysis.enable) return;
140
-
141
- const { resultsMap } = sourceContext.findings;
142
- const probesRules = [Rule.CMD_INJECTION, Rule.PATH_TRAVERSAL, Rule.SQL_INJECTION, Rule.XXE];
143
- const props = {};
144
-
145
- // Detecting probes
146
- Object.values(resultsMap).forEach(resultsByRuleId => {
147
- resultsByRuleId.forEach((resultByRuleId) => {
148
- const {
149
- ruleId,
150
- blocked,
151
- details,
152
- value,
153
- inputType
154
- } = resultByRuleId;
155
- if (blocked || !blocked && details.length > 0 || !probesRules.some(rule => rule === ruleId)) return;
156
-
157
- const { policy: { rulesMask } } = sourceContext;
158
-
159
- const results = (agentLib.scoreAtom(
160
- rulesMask,
161
- value,
162
- agentLib.InputType[inputType],
163
- {
164
- preferWorthWatching: false
165
- }
166
- ) || []).filter(({ score }) => score >= 90);
167
-
168
- if (!results.length) return;
169
-
170
- results.forEach(result => {
171
- const isAlreadyBlocked = (resultsMap[result.ruleId] || []).some(element =>
172
- element.blocked && element.inputType === inputType && element.value === value
173
- );
174
-
175
- if (isAlreadyBlocked) return;
176
-
177
- const probe = Object.assign({}, resultByRuleId, result, {
178
- mappedId: result.ruleId
179
- });
180
- const key = [probe.ruleId, probe.inputType, ...probe.path, probe.value].join('|');
181
- props[key] = probe;
182
- });
183
- });
184
- });
185
-
186
- Object.values(props).forEach(prop => {
187
- if (!resultsMap[prop.ruleId]) {
188
- resultsMap[prop.ruleId] = [];
189
- }
190
-
191
- resultsMap[prop.ruleId].push(prop);
192
- });
193
- };
194
-
195
- const jsonInputTypes = {
196
- keyType: agentLib.InputType.JsonKey, inputType: agentLib.InputType.JsonValue
197
- };
198
-
199
- const parameterInputTypes = {
200
- keyType: agentLib.InputType.ParameterKey, inputType: agentLib.InputType.ParameterValue
201
- };
202
-
203
144
  /**
204
145
  * handleQueryParams()
205
146
  *
@@ -225,7 +166,6 @@ module.exports = function(core) {
225
166
  return;
226
167
  }
227
168
 
228
-
229
169
  inputAnalysis.handleVirtualPatches(sourceContext, { PARAMETERS: queryParams });
230
170
 
231
171
  const block = commonObjectAnalyzer(sourceContext, queryParams, parameterInputTypes);
@@ -264,10 +204,13 @@ module.exports = function(core) {
264
204
  if (type !== 'Value') {
265
205
  return;
266
206
  }
207
+
267
208
  const items = agentLib.scoreAtom(rulesMask, value, UrlParameter, preferWW);
209
+
268
210
  if (!items) {
269
211
  return;
270
212
  }
213
+
271
214
  for (const item of items) {
272
215
  resultsList.push({
273
216
  ruleId: item.ruleId,
@@ -312,14 +255,15 @@ module.exports = function(core) {
312
255
  if (sourceContext.analyzedCookies) return;
313
256
  sourceContext.analyzedCookies = true;
314
257
 
258
+ inputAnalysis.handleVirtualPatches(sourceContext, { HEADERS: cookies });
259
+
260
+ const { policy: { rulesMask } } = sourceContext;
261
+
315
262
  const cookiesArr = Object.entries(cookies).reduce((acc, [key, value]) => {
316
263
  acc.push(key, value);
317
264
  return acc;
318
265
  }, []);
319
266
 
320
- inputAnalysis.handleVirtualPatches(sourceContext, { HEADERS: cookies });
321
-
322
- const { policy: { rulesMask } } = sourceContext;
323
267
  const cookieFindings = agentLib.scoreRequestConnect(rulesMask, { cookies: cookiesArr }, preferWW);
324
268
 
325
269
  const block = mergeFindings(sourceContext, cookieFindings);
@@ -340,6 +284,7 @@ module.exports = function(core) {
340
284
  */
341
285
  inputAnalysis.handleParsedBody = function(sourceContext, parsedBody) {
342
286
  if (sourceContext.analyzedBody) return;
287
+
343
288
  sourceContext.analyzedBody = true;
344
289
 
345
290
  if (typeof parsedBody !== 'object') {
@@ -358,6 +303,7 @@ module.exports = function(core) {
358
303
  bodyType = 'urlencoded';
359
304
  inputTypes = parameterInputTypes;
360
305
  }
306
+
361
307
  const block = commonObjectAnalyzer(sourceContext, parsedBody, inputTypes);
362
308
 
363
309
  sourceContext.findings.bodyType = bodyType;
@@ -478,6 +424,70 @@ module.exports = function(core) {
478
424
  }
479
425
  };
480
426
 
427
+ /**
428
+ * handleRequestEnd()
429
+ *
430
+ * Invoked when the request is complete.
431
+ *
432
+ * @param {Object} sourceContext
433
+ */
434
+ inputAnalysis.handleRequestEnd = function handleRequestEnd(sourceContext) {
435
+ if (!config.protect.probe_analysis.enable || sourceContext.allowed) return;
436
+
437
+ const { resultsMap } = sourceContext.findings;
438
+ const probesRules = [Rule.CMD_INJECTION, Rule.PATH_TRAVERSAL, Rule.SQL_INJECTION, Rule.XXE];
439
+ const props = {};
440
+
441
+ // Detecting probes
442
+ Object.values(resultsMap).forEach(resultsByRuleId => {
443
+ resultsByRuleId.forEach((resultByRuleId) => {
444
+ const {
445
+ ruleId,
446
+ blocked,
447
+ details,
448
+ value,
449
+ inputType
450
+ } = resultByRuleId;
451
+ if (blocked || !blocked && details.length > 0 || !probesRules.some(rule => rule === ruleId)) return;
452
+
453
+ const { policy: { rulesMask } } = sourceContext;
454
+
455
+ const results = (agentLib.scoreAtom(
456
+ rulesMask,
457
+ value,
458
+ agentLib.InputType[inputType],
459
+ {
460
+ preferWorthWatching: false
461
+ }
462
+ ) || []).filter(({ score }) => score >= 90);
463
+
464
+ if (!results.length) return;
465
+
466
+ results.forEach(result => {
467
+ const isAlreadyBlocked = (resultsMap[result.ruleId] || []).some(element =>
468
+ element.blocked && element.inputType === inputType && element.value === value
469
+ );
470
+
471
+ if (isAlreadyBlocked) return;
472
+
473
+ const probe = Object.assign({}, resultByRuleId, result, {
474
+ mappedId: result.ruleId
475
+ });
476
+ const key = [probe.ruleId, probe.inputType, ...probe.path, probe.value].join('|');
477
+ props[key] = probe;
478
+ });
479
+ });
480
+ });
481
+
482
+ Object.values(props).forEach(prop => {
483
+ if (!resultsMap[prop.ruleId]) {
484
+ resultsMap[prop.ruleId] = [];
485
+ }
486
+
487
+ resultsMap[prop.ruleId].push(prop);
488
+ });
489
+ };
490
+
481
491
  /**
482
492
  * commonObjectAnalyzer() walks an object supplied by the end-user and checks
483
493
  * it for vulnerabilities.
@@ -532,7 +542,9 @@ module.exports = function(core) {
532
542
  } else {
533
543
  itemType = inputType;
534
544
  }
545
+
535
546
  let items = agentLib.scoreAtom(rulesMask, value, itemType, preferWW);
547
+
536
548
  if (!items && !isMongoQueryType) {
537
549
  return;
538
550
  }
@@ -628,6 +640,66 @@ module.exports = function(core) {
628
640
  }
629
641
  };
630
642
 
643
+ /**
644
+ * Reads the source context's policy and compares to result item to check whether to ignore it.
645
+ * @param {ProtectMessage} sourceContext
646
+ * @param {Result} result
647
+ * @returns {boolean} whether result should be excluded
648
+ */
649
+ function isResultExcluded(sourceContext, result) {
650
+ const { policy: { exclusions } } = sourceContext;
651
+ const { ruleId, path, inputType, value } = result;
652
+ const inputName = path ? path[path.length - 1] : null;
653
+
654
+ let checkCookiesInHeader = false;
655
+ let inputExclusions;
656
+ switch (inputType) {
657
+ case 'JsonKey':
658
+ case 'JsonValue':
659
+ case 'MultipartName': {
660
+ return exclusions.ignoreBody || exclusions.bodyPolicy?.[ruleId] === OFF;
661
+ }
662
+ case 'ParameterKey':
663
+ case 'ParameterValue': {
664
+ const qsExcluded = exclusions.ignoreQuerystring || exclusions.querystringPolicy?.[ruleId] === OFF;
665
+ if (qsExcluded) return true;
666
+ inputExclusions = exclusions.parameter;
667
+ break;
668
+ }
669
+ case 'CookieValue': {
670
+ inputExclusions = exclusions.cookie;
671
+ break;
672
+ }
673
+ case 'HeaderKey':
674
+ case 'HeaderValue': {
675
+ if (path?.[0]?.toLowerCase() === 'cookie') {
676
+ inputExclusions = exclusions.cookie;
677
+ checkCookiesInHeader = true;
678
+ } else {
679
+ inputExclusions = exclusions.header;
680
+ }
681
+ break;
682
+ }
683
+ }
684
+
685
+ if (!inputName || !inputExclusions) return false;
686
+
687
+ for (const excl of inputExclusions) {
688
+ let nameCheck = false;
689
+ if (checkCookiesInHeader) {
690
+ nameCheck = excl.checkCookiesInHeader(value);
691
+ } else {
692
+ nameCheck = excl.matchesInputName(inputName);
693
+ }
694
+ if (!nameCheck) continue;
695
+ if (!excl.policy || excl.policy[ruleId] === OFF) {
696
+ return true;
697
+ }
698
+ }
699
+
700
+ return false;
701
+ }
702
+
631
703
  /**
632
704
  * merge new findings into the existing findings
633
705
  *
@@ -641,6 +713,11 @@ function mergeFindings(sourceContext, newFindings) {
641
713
  if (!newFindings.trackRequest) {
642
714
  return findings.securityException;
643
715
  }
716
+
717
+ newFindings.resultsList = newFindings.resultsList.filter(
718
+ (result) => !isResultExcluded(sourceContext, result)
719
+ );
720
+
644
721
  normalizeFindings(policy, newFindings);
645
722
 
646
723
  findings.trackRequest = findings.trackRequest || newFindings.trackRequest;
@@ -176,8 +176,13 @@ class HttpInstrumentation {
176
176
  setImmediate(() => method.call(instance, ...args));
177
177
  return;
178
178
  }
179
-
180
179
  store.protect = this.makeSourceContext(req, res);
180
+
181
+ if (store.protect.allowed) {
182
+ setImmediate(() => method.call(instance, ...args));
183
+ return;
184
+ }
185
+
181
186
  const { reqData } = store.protect;
182
187
 
183
188
  res.on('finish', () => {
@@ -26,7 +26,7 @@ module.exports = (core) => {
26
26
  const virtualPatchesEvaluators = inputAnalysis.virtualPatchesEvaluators = [];
27
27
 
28
28
  messages.on(Event.SERVER_SETTINGS_UPDATE, (serverUpdate) => {
29
- const virtualPatches = serverUpdate.settings?.defend.virtualPatches;
29
+ const virtualPatches = serverUpdate.settings?.defend?.virtualPatches;
30
30
  if (virtualPatches) {
31
31
  buildVPEvaluators(virtualPatches, virtualPatchesEvaluators);
32
32
  }
@@ -16,7 +16,9 @@
16
16
  'use strict';
17
17
 
18
18
  module.exports = function(core) {
19
- const { protect } = core;
19
+ const {
20
+ protect: { getPolicy }
21
+ } = core;
20
22
 
21
23
  function makeSourceContext(req, res) {
22
24
  // make the abstract request. it is an abstraction of a request that
@@ -27,8 +29,10 @@ module.exports = function(core) {
27
29
  // or res objects so that all data coupling is all defined here.
28
30
 
29
31
  // separate path and search params
30
- const ix = req.url.indexOf('?');
32
+
31
33
  let uriPath, queries;
34
+ const ix = req.url.indexOf('?');
35
+
32
36
  if (ix >= 0) {
33
37
  uriPath = req.url.slice(0, ix);
34
38
  queries = req.url.slice(ix + 1);
@@ -36,6 +40,14 @@ module.exports = function(core) {
36
40
  uriPath = req.url;
37
41
  queries = '';
38
42
  }
43
+
44
+ const policy = getPolicy({ uriPath });
45
+
46
+ // URL exclusions can disable all rules
47
+ if (!policy) {
48
+ return { allowed: true };
49
+ }
50
+
39
51
  // lowercase header keys and capture content-type
40
52
  let contentType = '';
41
53
  const headers = Array(req.rawHeaders.length);
@@ -80,7 +92,7 @@ module.exports = function(core) {
80
92
  // block closure captures res so it isn't exposed to beyond here
81
93
  block: core.protect.makeResponseBlocker(res),
82
94
 
83
- policy: protect.getPolicy(),
95
+ policy,
84
96
 
85
97
  exclusions: [],
86
98
  virtualPatchesEvaluators: [],
package/lib/policy.js CHANGED
@@ -27,14 +27,72 @@ const {
27
27
  Event: { SERVER_SETTINGS_UPDATE },
28
28
  } = require('@contrast/common');
29
29
 
30
-
31
30
  module.exports = function(core) {
32
- const { config, logger, messages, protect } = core;
33
- const policy = protect.policy = {};
31
+ const {
32
+ config,
33
+ logger,
34
+ messages,
35
+ protect,
36
+ protect: { agentLib }
37
+ } = core;
38
+
39
+ const compiled = {
40
+ url: [],
41
+ querystring: [],
42
+ header: [],
43
+ body: [],
44
+ cookie: [],
45
+ parameter: [],
46
+ };
47
+
48
+ const policy = protect.policy = {
49
+ exclusions: compiled
50
+ };
51
+
52
+ function regExpCheck(str) {
53
+ return str.indexOf('*') > 0 ||
54
+ str.indexOf('.') > 0 ||
55
+ str.indexOf('+') > 0 ||
56
+ str.indexOf('?') > 0 ||
57
+ str.indexOf('\\') > 0;
58
+ }
59
+
60
+ function buildUriPathRegExp(urls) {
61
+ let regExpNeeded = false;
62
+ for (const url of urls) {
63
+ if (regExpCheck(url)) {
64
+ regExpNeeded = true;
65
+ }
66
+ }
67
+ if (regExpNeeded) {
68
+ const rx = new RegExp(`^${urls.join('|')}$`);
69
+
70
+ return (uriPath) => rx ? rx.test(uriPath) : false;
71
+ }
72
+
73
+ return (uriPath) => urls.some((url) => url === uriPath);
74
+ }
75
+
76
+ function createUriPathMatcher(urls) {
77
+ if (urls.length) {
78
+ return buildUriPathRegExp(urls);
79
+ } else {
80
+ return () => true;
81
+ }
82
+ }
83
+
84
+ function createInputNameMatcher(dtmInputName) {
85
+ if (regExpCheck(dtmInputName)) {
86
+ const rx = new RegExp(`^${dtmInputName}$`);
87
+ return (inputName) => rx ? rx.test(inputName) : false;
88
+ }
89
+
90
+ return (inputName) => inputName === dtmInputName;
91
+ }
34
92
 
35
93
  function getModeFromConfig(ruleId) {
36
94
  if (config.protect.disabled_rules.includes(ruleId)) {
37
- return 'off';
95
+ return OFF;
38
96
  }
39
97
  return config.protect.rules?.[ruleId]?.mode;
40
98
  }
@@ -84,11 +142,20 @@ module.exports = function(core) {
84
142
  */
85
143
  function updateRulesMask() {
86
144
  let rulesMask = 0;
87
- for (const [ruleId, mode] of Object.entries(policy)) {
145
+
146
+ for (const entry of Object.entries(policy)) {
147
+ let [ruleId] = entry;
148
+ const [, mode] = entry;
149
+
150
+ if (ruleId === 'nosql-injection') {
151
+ ruleId = 'nosql-injection-mongo';
152
+ }
153
+
88
154
  if (protect.agentLib.RuleType[ruleId] && mode !== OFF) {
89
155
  rulesMask = rulesMask | protect.agentLib.RuleType[ruleId];
90
156
  }
91
157
  }
158
+
92
159
  policy.rulesMask = rulesMask;
93
160
  }
94
161
 
@@ -96,13 +163,83 @@ module.exports = function(core) {
96
163
  * This gets called by protect.makeSourceContext(). We return copy of policy to avoid
97
164
  * inconsistent behavior if policy is updated during request handling.
98
165
  */
99
- function getPolicy() {
100
- return { ...policy };
101
- }
166
+ function getPolicy({ uriPath } = {}) {
167
+ const requestPolicy = {
168
+ exclusions: {
169
+ ignoreQuerystring: false,
170
+ querystringPolicy: null,
171
+ ignoreBody: false,
172
+ bodyPolicy: null,
173
+ header: [],
174
+ cookie: [],
175
+ parameter: [],
176
+ },
177
+ rulesMask: policy.rulesMask,
178
+ };
102
179
 
103
- initPolicy();
180
+ for (const ruleId of Object.values(Rule)) {
181
+ requestPolicy[ruleId] = policy[ruleId];
182
+ }
104
183
 
105
- messages.on(SERVER_SETTINGS_UPDATE, (remoteSettings) => {
184
+ // handle exclusions
185
+ for (const [inputType, exclusions] of Object.entries(compiled)) {
186
+ for (const e of exclusions) {
187
+ if (!e.matchesUriPath(uriPath)) continue;
188
+
189
+ // url exclusions
190
+ if (inputType === 'url') {
191
+ // if applies to all rules, there is no policy for the request i.e. disable protect
192
+ if (!e.policy) {
193
+ return null;
194
+ }
195
+
196
+ // merge exclusion's policy into the request's policy
197
+ for (const key of Object.keys(e.policy)) {
198
+ const value = e.policy[key];
199
+ if (key === 'rulesMask') {
200
+ // this is how to disable rules bitwise
201
+ requestPolicy.rulesMask = requestPolicy.rulesMask & ~value;
202
+ } else {
203
+ requestPolicy[key] = value;
204
+ }
205
+ }
206
+ } else if (inputType === 'querystring') {
207
+ if (!e.policy) {
208
+ requestPolicy.exclusions.ignoreQuerystring = true;
209
+ } else {
210
+ // merge exclusion's policy into the querystring's policy
211
+ requestPolicy.exclusions.querystringPolicy = requestPolicy.exclusions.querystringPolicy || {};
212
+ for (const key of Object.keys(e.policy)) {
213
+ const value = e.policy[key];
214
+ if (key !== 'rulesMask') {
215
+ requestPolicy.exclusions.querystringPolicy[key] = value;
216
+ }
217
+ }
218
+ }
219
+ } else if (inputType === 'body') {
220
+ if (!e.policy) {
221
+ requestPolicy.exclusions.ignoreBody = true;
222
+ } else {
223
+ // merge exclusion's policy into the querystring's policy
224
+ requestPolicy.exclusions.bodyPolicy = requestPolicy.exclusions.bodyPolicy || {};
225
+ for (const key of Object.keys(e.policy)) {
226
+ const value = e.policy[key];
227
+ if (key !== 'rulesMask') {
228
+ requestPolicy.exclusions.bodyPolicy[key] = value;
229
+ }
230
+ }
231
+ }
232
+ } else {
233
+ // copy matching input exclusions into request policy
234
+ requestPolicy.exclusions[inputType].push(e);
235
+ }
236
+ }
237
+ }
238
+
239
+ return requestPolicy;
240
+ }
241
+
242
+ function updateGlobalPolicy(remoteSettings) {
106
243
  let update;
107
244
 
108
245
  const protectionRules = remoteSettings?.settings?.defend?.protectionRules;
@@ -128,7 +265,79 @@ module.exports = function(core) {
128
265
  updateRulesMask();
129
266
  logger.info({ policy: protect.policy }, `protect policy updated from ${update}`);
130
267
  }
268
+ }
269
+
270
+ function updateExclusions(serverUpdate) {
271
+ const exclusions = [
272
+ ...(serverUpdate.settings?.exceptions?.inputExceptions || []),
273
+ ...(serverUpdate.settings?.exceptions?.urlExceptions || [])
274
+ ].filter((exclusion) => exclusion.modes.includes('defend'));
275
+
276
+ if (!exclusions.length) return;
277
+
278
+ for (const exclusionDtm of exclusions) {
279
+ exclusionDtm.inputType = exclusionDtm.inputType || 'URL';
280
+
281
+ const { name, rules, inputName, urls, inputType } = exclusionDtm;
282
+ const key = inputType.toLowerCase();
283
+
284
+ if (!compiled[key]) continue;
285
+
286
+ try {
287
+ const e = { name };
288
+ e.matchesUriPath = createUriPathMatcher(urls);
289
+
290
+ if (inputName) {
291
+ e.matchesInputName = createInputNameMatcher(inputName);
292
+ }
293
+
294
+ if (rules.length) {
295
+ let rulesMask = 0;
296
+ const exclusionPolicy = {};
297
+
298
+ for (let ruleId of rules) {
299
+ // todo: this doesn't seem to make a difference?
300
+ if (ruleId === 'nosql-injection') {
301
+ ruleId = 'nosql-injection-mongo';
302
+ }
303
+
304
+ if (agentLib.RuleType[ruleId]) {
305
+ Object.assign(exclusionPolicy, { [ruleId]: OFF });
306
+ if (inputType === 'URL') {
307
+ rulesMask = rulesMask | agentLib.RuleType[ruleId];
308
+ exclusionPolicy.rulesMask = rulesMask;
309
+ }
310
+ }
311
+ }
312
+
313
+ e.policy = exclusionPolicy;
314
+ }
315
+ if (key === 'cookie') {
316
+ e.checkCookieInHeader = (cookieHeader) => {
317
+ for (const cookiePair of cookieHeader.split(';')) {
318
+ const cookieKey = cookiePair.split('=')[0];
319
+ if (e.matchesInputName(cookieKey)) {
320
+ return true;
321
+ }
322
+ }
323
+
324
+ return false;
325
+ };
326
+ }
327
+
328
+ compiled[key].push(e);
329
+ } catch (err) {
330
+ logger.error({ err, exclusionDtm }, 'failed to process exclusion');
331
+ }
332
+ }
333
+ }
334
+
335
+ messages.on(SERVER_SETTINGS_UPDATE, (msg) => {
336
+ updateGlobalPolicy(msg);
337
+ updateExclusions(msg);
131
338
  });
132
339
 
340
+ initPolicy();
341
+
133
342
  return protect.getPolicy = getPolicy;
134
343
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/protect",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Contrast service providing framework-agnostic Protect support",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",