@contrast/assess 1.27.2 → 1.28.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 (63) hide show
  1. package/lib/constants.js +3 -3
  2. package/lib/crypto-analysis/install/crypto.js +1 -1
  3. package/lib/dataflow/propagation/install/JSON/parse.js +2 -2
  4. package/lib/dataflow/propagation/install/JSON/stringify.js +10 -11
  5. package/lib/dataflow/propagation/install/array-prototype-join.js +10 -12
  6. package/lib/dataflow/propagation/install/buffer.js +3 -1
  7. package/lib/dataflow/propagation/install/contrast-methods/add.js +73 -72
  8. package/lib/dataflow/propagation/install/contrast-methods/number.js +4 -5
  9. package/lib/dataflow/propagation/install/contrast-methods/string.js +3 -2
  10. package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -6
  11. package/lib/dataflow/propagation/install/joi/boolean.js +2 -4
  12. package/lib/dataflow/propagation/install/joi/expression.js +2 -4
  13. package/lib/dataflow/propagation/install/joi/index.js +1 -1
  14. package/lib/dataflow/propagation/install/joi/number.js +2 -4
  15. package/lib/dataflow/propagation/install/joi/string-schema.js +8 -3
  16. package/lib/dataflow/propagation/install/joi/values.js +5 -1
  17. package/lib/dataflow/propagation/install/path/format.js +7 -4
  18. package/lib/dataflow/propagation/install/path/parse.js +4 -5
  19. package/lib/dataflow/propagation/install/querystring/escape.js +1 -1
  20. package/lib/dataflow/propagation/install/querystring/parse.js +6 -7
  21. package/lib/dataflow/propagation/install/querystring/stringify.js +1 -1
  22. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +2 -3
  23. package/lib/dataflow/propagation/install/string/concat.js +22 -23
  24. package/lib/dataflow/propagation/install/string/html-methods.js +6 -7
  25. package/lib/dataflow/propagation/install/string/index.js +3 -3
  26. package/lib/dataflow/propagation/install/string/match-all.js +10 -15
  27. package/lib/dataflow/propagation/install/string/match.js +5 -4
  28. package/lib/dataflow/propagation/install/string/replace.js +22 -16
  29. package/lib/dataflow/propagation/install/string/slice.js +7 -6
  30. package/lib/dataflow/propagation/install/string/split.js +17 -16
  31. package/lib/dataflow/propagation/install/string/substring.js +9 -8
  32. package/lib/dataflow/propagation/install/string/trim.js +4 -5
  33. package/lib/dataflow/propagation/install/url/parse.js +1 -1
  34. package/lib/dataflow/propagation/install/url/searchParams.js +2 -1
  35. package/lib/dataflow/propagation/install/url/url.js +1 -1
  36. package/lib/dataflow/sinks/install/child-process.js +1 -1
  37. package/lib/dataflow/sinks/install/express/reflected-xss.js +7 -5
  38. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +1 -2
  39. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +1 -3
  40. package/lib/dataflow/sinks/install/fs.js +1 -1
  41. package/lib/dataflow/sinks/install/function.js +1 -1
  42. package/lib/dataflow/sinks/install/hapi/unvalidated-redirect.js +1 -2
  43. package/lib/dataflow/sinks/install/http/request.js +6 -5
  44. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +2 -2
  45. package/lib/dataflow/sinks/install/libxmljs.js +1 -1
  46. package/lib/dataflow/sinks/install/marsdb.js +1 -2
  47. package/lib/dataflow/sinks/install/mongodb.js +46 -9
  48. package/lib/dataflow/sinks/install/mysql.js +1 -1
  49. package/lib/dataflow/sinks/install/postgres.js +1 -3
  50. package/lib/dataflow/sinks/install/sequelize.js +1 -2
  51. package/lib/dataflow/sinks/install/vm.js +1 -1
  52. package/lib/dataflow/sources/install/body-parser1.js +12 -5
  53. package/lib/dataflow/sources/install/cookie-parser1.js +4 -3
  54. package/lib/dataflow/sources/install/qs6.js +7 -5
  55. package/lib/dataflow/sources/install/querystring.js +8 -2
  56. package/lib/dataflow/tag-utils.js +22 -5
  57. package/lib/get-source-context.js +2 -1
  58. package/lib/index.js +13 -0
  59. package/lib/session-configuration/install/express-session.js +1 -3
  60. package/lib/session-configuration/install/fastify-cookie.js +1 -1
  61. package/lib/session-configuration/install/hapi.js +1 -3
  62. package/lib/session-configuration/install/koa.js +1 -1
  63. package/package.json +2 -2
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { inspect } = require('@contrast/common');
19
18
  const { patchType } = require('../../common');
20
19
 
21
20
  module.exports = function(core) {
@@ -24,6 +23,7 @@ module.exports = function(core) {
24
23
  patcher,
25
24
  depHooks,
26
25
  assess: {
26
+ inspect, // todo: remove
27
27
  eventFactory: { createPropagationEvent },
28
28
  dataflow: { tracker }
29
29
  }
@@ -19,7 +19,6 @@ const {
19
19
  join,
20
20
  Rule: { CMD_INJECTION: ruleId },
21
21
  isString,
22
- inspect,
23
22
  } = require('@contrast/common');
24
23
  const { InstrumentationType: { RULE } } = require('../../../constants');
25
24
  const { patchType } = require('../common');
@@ -35,6 +34,7 @@ module.exports = function(core) {
35
34
  depHooks,
36
35
  patcher,
37
36
  assess: {
37
+ inspect, // todo: remove
38
38
  getSourceContext,
39
39
  eventFactory: { createSinkEvent },
40
40
  dataflow: {
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  Rule: { REFLECTED_XSS: ruleId },
21
20
  DataflowTag: {
@@ -52,14 +51,13 @@ module.exports = function(core) {
52
51
  eventFactory: { createSinkEvent },
53
52
  dataflow: {
54
53
  tracker,
55
- sinks: { isVulnerable, reportFindings, reportSafePositive }
54
+ sinks: { isVulnerable, reportFindings, reportSafePositive, isSafeContentType }
56
55
  },
57
56
  ruleScopes,
58
57
  },
59
58
  } = core;
60
59
 
61
60
  const reflectedXss = core.assess.dataflow.sinks.express.reflectedXss = {};
62
- const inspect = patcher.unwrap(util.inspect);
63
61
 
64
62
  const safeTags = [
65
63
  `excluded:${ruleId}`,
@@ -81,7 +79,11 @@ module.exports = function(core) {
81
79
  name,
82
80
  patchType,
83
81
  around: (next, data) => {
84
- if (!getSourceContext(RULE, ruleId)) return next();
82
+ const sourceContext = getSourceContext(RULE, ruleId);
83
+ if (!sourceContext) return next();
84
+
85
+ const { contentType } = sourceContext.responseData;
86
+ if (contentType && isSafeContentType(contentType)) return next();
85
87
 
86
88
  const [str] = data.args;
87
89
 
@@ -96,7 +98,7 @@ module.exports = function(core) {
96
98
  tracked: true,
97
99
  value: strInfo.value,
98
100
  }],
99
- context: `response.${method}(${inspect(strInfo.value)})`,
101
+ context: `response.${method}('${strInfo.value}')`,
100
102
  history: [strInfo],
101
103
  name,
102
104
  moduleName: 'express',
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  Rule: { UNVALIDATED_REDIRECT: ruleId },
21
20
  DataflowTag: {
@@ -46,6 +45,7 @@ module.exports = function(core) {
46
45
  patcher,
47
46
  config,
48
47
  assess: {
48
+ inspect, // todo: remove
49
49
  getSourceContext,
50
50
  eventFactory: { createSinkEvent },
51
51
  dataflow: {
@@ -56,7 +56,6 @@ module.exports = function(core) {
56
56
  } = core;
57
57
 
58
58
  const unvalidatedRedirect = core.assess.dataflow.sinks.express.unvalidatedRedirect = {};
59
- const inspect = patcher.unwrap(util.inspect);
60
59
 
61
60
  const safeTags = [
62
61
  `excluded:${ruleId}`,
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  Rule: { UNVALIDATED_REDIRECT: ruleId },
21
20
  DataflowTag: {
@@ -65,6 +64,7 @@ module.exports = function(core) {
65
64
  depHooks,
66
65
  patcher,
67
66
  assess: {
67
+ inspect, // todo: remove
68
68
  getSourceContext,
69
69
  eventFactory: { createSinkEvent },
70
70
  dataflow: {
@@ -76,8 +76,6 @@ module.exports = function(core) {
76
76
  const unvalidatedRedirect =
77
77
  (core.assess.dataflow.sinks.fastify.unvalidatedRedirect = {});
78
78
 
79
- const inspect = patcher.unwrap(util.inspect);
80
-
81
79
  const safeTags = [
82
80
  `excluded:${ruleId}`,
83
81
  CUSTOM_ENCODED,
@@ -25,7 +25,6 @@ const {
25
25
  },
26
26
  FS_METHODS,
27
27
  Rule: { PATH_TRAVERSAL: ruleId },
28
- inspect,
29
28
  isString,
30
29
  join,
31
30
  } = require('@contrast/common');
@@ -36,6 +35,7 @@ module.exports = function(core) {
36
35
  depHooks,
37
36
  patcher,
38
37
  assess: {
38
+ inspect, // todo: remove
39
39
  getSourceContext,
40
40
  eventFactory: { createSinkEvent },
41
41
  dataflow: {
@@ -17,7 +17,6 @@
17
17
 
18
18
  const {
19
19
  isString,
20
- inspect,
21
20
  join,
22
21
  DataflowTag: {
23
22
  UNTRUSTED,
@@ -47,6 +46,7 @@ module.exports = function (core) {
47
46
  logger,
48
47
  patcher,
49
48
  assess: {
49
+ inspect, // todo: remove
50
50
  getSourceContext,
51
51
  eventFactory: { createSinkEvent },
52
52
  dataflow: {
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  Rule: { UNVALIDATED_REDIRECT: ruleId },
21
20
  DataflowTag: {
@@ -46,6 +45,7 @@ module.exports = function(core) {
46
45
  patcher,
47
46
  config,
48
47
  assess: {
48
+ inspect, // todo: remove
49
49
  getSourceContext,
50
50
  eventFactory: { createSinkEvent },
51
51
  dataflow: {
@@ -56,7 +56,6 @@ module.exports = function(core) {
56
56
  } = core;
57
57
 
58
58
  const unvalidatedRedirect = core.assess.dataflow.sinks.hapi.unvalidatedRedirect = {};
59
- const inspect = patcher.unwrap(util.inspect);
60
59
 
61
60
  const safeTags = [
62
61
  `excluded:${ruleId}`,
@@ -17,7 +17,6 @@
17
17
 
18
18
  const Url = require('url');
19
19
  const {
20
- inspect,
21
20
  isString,
22
21
  DataflowTag: {
23
22
  UNTRUSTED,
@@ -46,6 +45,7 @@ module.exports = function(core) {
46
45
  depHooks,
47
46
  patcher,
48
47
  assess: {
48
+ inspect, // todo: remove
49
49
  getSourceContext,
50
50
  eventFactory: { createSinkEvent },
51
51
  dataflow: {
@@ -104,11 +104,12 @@ module.exports = function(core) {
104
104
  pre(data) {
105
105
  if (!getSourceContext(RULE, ruleId)) return;
106
106
 
107
- const [req] = data.args;
108
- if (!req) return;
107
+ // url <string> |<URL>
108
+ const [urlArg] = data.args;
109
+ if (!urlArg) return;
109
110
 
110
111
  ['host', 'hostname', 'localAddress', 'protocol'].forEach((key) => {
111
- const value = getValueFromReq(req, key);
112
+ const value = getValueFromReq(urlArg, key);
112
113
  if (!value) return;
113
114
 
114
115
  const strInfo = tracker.getData(value);
@@ -116,7 +117,7 @@ module.exports = function(core) {
116
117
 
117
118
  if (containsTrustedLib(strInfo.stack)) return;
118
119
 
119
- const arg0 = isString(req) ? req : inspect(req);
120
+ const arg0 = isString(urlArg) ? urlArg : inspect(urlArg);
120
121
  const idx = arg0.indexOf(value);
121
122
  const urlTags = createAppendTags({}, strInfo.tags, idx);
122
123
 
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  DataflowTag: {
21
20
  UNTRUSTED,
@@ -47,6 +46,7 @@ module.exports = function(core) {
47
46
  patcher,
48
47
  config,
49
48
  assess: {
49
+ inspect, // todo: remove
50
50
  getSourceContext,
51
51
  eventFactory: { createSinkEvent },
52
52
  dataflow: {
@@ -56,7 +56,7 @@ module.exports = function(core) {
56
56
  },
57
57
  } = core;
58
58
 
59
- const inspect = patcher.unwrap(util.inspect);
59
+
60
60
  const safeTags = [
61
61
  `excluded:${ruleId}`,
62
62
  CUSTOM_ENCODED,
@@ -23,7 +23,6 @@ const {
23
23
  ALPHANUM_SPACE_HYPHEN,
24
24
  LIMITED_CHARS,
25
25
  },
26
- inspect
27
26
  } = require('@contrast/common');
28
27
  const { InstrumentationType: { RULE } } = require('../../../constants');
29
28
  const { patchType } = require('../common');
@@ -48,6 +47,7 @@ module.exports = function(core) {
48
47
  depHooks,
49
48
  patcher,
50
49
  assess: {
50
+ inspect, // todo: remove
51
51
  getSourceContext,
52
52
  eventFactory: { createSinkEvent },
53
53
  dataflow: {
@@ -14,7 +14,6 @@
14
14
  */
15
15
  'use strict';
16
16
 
17
- const util = require('util');
18
17
  const {
19
18
  traverseValues,
20
19
  Rule: { NOSQL_INJECTION_MONGO: ruleId },
@@ -51,6 +50,7 @@ module.exports = function (core) {
51
50
  logger,
52
51
  patcher,
53
52
  assess: {
53
+ inspect, // todo: remove
54
54
  getSourceContext,
55
55
  eventFactory: { createSinkEvent },
56
56
  dataflow: {
@@ -61,7 +61,6 @@ module.exports = function (core) {
61
61
  } = core;
62
62
 
63
63
  const instr = core.assess.dataflow.sinks.marsdb = {};
64
- const inspect = patcher.unwrap(util.inspect);
65
64
 
66
65
  function getVulnerabilityInfo(query) {
67
66
  let vulnInfo = null;
@@ -28,7 +28,6 @@ const {
28
28
  isNonEmptyObject,
29
29
  traverseValues,
30
30
  isString,
31
- inspect
32
31
  } = require('@contrast/common');
33
32
  const { InstrumentationType: { RULE } } = require('../../../constants');
34
33
  const utils = require('../../tag-utils');
@@ -83,6 +82,7 @@ module.exports = function (core) {
83
82
  logger,
84
83
  patcher,
85
84
  assess: {
85
+ inspect, // todo: remove
86
86
  getSourceContext,
87
87
  eventFactory: { createSinkEvent },
88
88
  dataflow: {
@@ -236,6 +236,49 @@ module.exports = function (core) {
236
236
  return { vulnInfo, reportSafe };
237
237
  };
238
238
 
239
+ /**
240
+ * Truncate `arg` values for reporting and get the adjusted `tags`.
241
+ * If an argument isn't directly relevant to the vulnerability, we report it by its type.
242
+ * If it is relevant, we truncate the value if it is an object or array, but not if it is a string.
243
+ * Ex1: Array truncations will look like `[...'<key>':'<value>'...]`
244
+ * Ex2: Object truncations will look like `{...'<key>':'<value>'...}`
245
+ * In the above examples, `key` will be the last value in the path array, and `value` should be the
246
+ * containing the injection. So whether the argument is a string or an object, the actual string value
247
+ * leading to the injection will not be truncated.
248
+ */
249
+ function getAdjustedFields(origArgs, vulnInfo, vulnArgIdx) {
250
+ const { path, strInfo } = vulnInfo;
251
+ const coercedArgs = [];
252
+ let prefix = '';
253
+ let suffix = '';
254
+
255
+ if (path) {
256
+ let openChar, closeChar;
257
+ if (Array.isArray(origArgs[vulnArgIdx])) {
258
+ openChar = '[';
259
+ closeChar = ']';
260
+ } else {
261
+ openChar = '{';
262
+ closeChar = '}';
263
+ }
264
+ prefix = `${openChar}...'${path[path.length - 1]}':'`;
265
+ suffix = `'...${closeChar}`;
266
+ }
267
+
268
+ for (let i = 0; i < origArgs.length; i++) {
269
+ if (i === vulnArgIdx) {
270
+ coercedArgs.push({ value: `${prefix}${strInfo.value}${suffix}`, tracked: true });
271
+ } else {
272
+ coercedArgs.push({ value: origArgs[i]?.constructor?.name ?? typeof origArgs[i], tracked: false });
273
+ }
274
+ }
275
+
276
+ return {
277
+ args: coercedArgs,
278
+ tags: prefix ? utils.createAppendTags(null, strInfo.tags, prefix.length) : strInfo.tags,
279
+ };
280
+ }
281
+
239
282
  function createAroundHook(entity, name, method, getInfoMethod, vulnerableArgIdxs) {
240
283
  const argsIdxsToCheck = vulnerableArgIdxs || [0];
241
284
  return function (next, data) {
@@ -290,19 +333,13 @@ module.exports = function (core) {
290
333
  : next();
291
334
  }
292
335
 
293
- const { path, strInfo } = vulnInfo;
294
336
  const objName = getObjectName(obj, entity);
295
- const args = origArgs.map((arg, idx) => ({
296
- value: isString(arg) ? arg : inspect(arg, { depth: 4 }),
297
- tracked: idx === vulnArgIdx,
298
- }));
299
-
300
- const tags = path ? utils.createAdjustedQueryTags(path, strInfo.tags, strInfo.value, args[vulnArgIdx].value) : strInfo?.tags;
337
+ const { args, tags } = getAdjustedFields(origArgs, vulnInfo, vulnArgIdx);
301
338
  const resultVal = args[args.length - 1].value.startsWith('[Function') ? '' : 'Promise';
302
339
  const sinkEvent = createSinkEvent({
303
340
  args,
304
341
  context: `${objName}.${method}(${args.map((a, idx) => isString(origArgs[idx]) ? `'${a.value}'` : a.value)})`,
305
- history: [strInfo],
342
+ history: [vulnInfo.strInfo],
306
343
  object: {
307
344
  tracked: false,
308
345
  value: `mongodb.${entity}`,
@@ -28,7 +28,6 @@ const {
28
28
  UNTRUSTED
29
29
  },
30
30
  isString,
31
- inspect,
32
31
  } = require('@contrast/common');
33
32
  const { InstrumentationType: { RULE } } = require('../../../constants');
34
33
 
@@ -54,6 +53,7 @@ module.exports = function(core) {
54
53
  depHooks,
55
54
  patcher,
56
55
  assess: {
56
+ inspect, // todo: remove
57
57
  getSourceContext,
58
58
  eventFactory: { createSinkEvent },
59
59
  dataflow: {
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  DataflowTag: {
21
20
  CUSTOM_VALIDATED,
@@ -43,6 +42,7 @@ module.exports = function(core) {
43
42
  depHooks,
44
43
  patcher,
45
44
  assess: {
45
+ inspect, // todo: remove
46
46
  getSourceContext,
47
47
  eventFactory: { createSinkEvent },
48
48
  dataflow: {
@@ -60,8 +60,6 @@ module.exports = function(core) {
60
60
  CUSTOM_ENCODED,
61
61
  ];
62
62
 
63
- const inspect = patcher.unwrap(util.inspect);
64
-
65
63
  const postgres = core.assess.dataflow.sinks.postgres = {};
66
64
 
67
65
  const preHook = (methodSignature) => (data) => {
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  Rule: { SQL_INJECTION: ruleId },
21
20
  DataflowTag: {
@@ -42,6 +41,7 @@ module.exports = function (core) {
42
41
  patcher,
43
42
  config,
44
43
  assess: {
44
+ inspect, // todo: remove
45
45
  getSourceContext,
46
46
  eventFactory: { createSinkEvent },
47
47
  dataflow: {
@@ -59,7 +59,6 @@ module.exports = function (core) {
59
59
  CUSTOM_ENCODED
60
60
  ];
61
61
  const requiredTag = UNTRUSTED;
62
- const inspect = patcher.unwrap(util.inspect);
63
62
 
64
63
  const sequelize = (core.assess.dataflow.sinks.sequelize = {});
65
64
 
@@ -25,7 +25,6 @@ const {
25
25
  LIMITED_CHARS,
26
26
  },
27
27
  Rule: { UNSAFE_CODE_EXECUTION: ruleId },
28
- inspect,
29
28
  isNonEmptyObject,
30
29
  isString,
31
30
  join,
@@ -56,6 +55,7 @@ module.exports = function (core) {
56
55
  depHooks,
57
56
  patcher,
58
57
  assess: {
58
+ inspect, // todo: remove
59
59
  getSourceContext,
60
60
  eventFactory: { createSinkEvent },
61
61
  dataflow: {
@@ -16,6 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../constants');
19
20
  const { patchType } = require('../common');
20
21
 
21
22
  const METHODS = ['json', 'raw', 'text', 'urlencoded'];
@@ -27,12 +28,18 @@ const INPUT_TYPES = {
27
28
  };
28
29
 
29
30
  module.exports = function init(core) {
30
- const { assess, depHooks, logger, patcher, scopes } = core;
31
+ const {
32
+ scopes,
33
+ assess: { dataflow, getSourceContext },
34
+ depHooks,
35
+ logger,
36
+ patcher,
37
+ } = core;
31
38
 
32
39
  const preHook = (data) => {
33
40
  const [req, , next] = data.args;
34
41
  data.args[2] = scopes.wrap(function contrastNext(...args) {
35
- const sourceContext = scopes.sources.getStore()?.assess;
42
+ const sourceContext = getSourceContext(SOURCE);
36
43
 
37
44
  if (!sourceContext) {
38
45
  logger.error({ funcKey: data.funcKey }, 'unable to handle source. Missing `sourceContext`');
@@ -68,7 +75,7 @@ module.exports = function init(core) {
68
75
  }
69
76
 
70
77
  try {
71
- assess.dataflow.sources.handle({
78
+ dataflow.sources.handle({
72
79
  context,
73
80
  data: _data,
74
81
  name: data.name,
@@ -89,7 +96,7 @@ module.exports = function init(core) {
89
96
  });
90
97
  };
91
98
 
92
- assess.dataflow.sources.bodyParser1Instrumentation = {
99
+ core.assess.dataflow.sources.bodyParser1Instrumentation = {
93
100
  install() {
94
101
  depHooks.resolve(
95
102
  { name: 'body-parser', version: '>=1.0.0' },
@@ -127,5 +134,5 @@ module.exports = function init(core) {
127
134
  }
128
135
  };
129
136
 
130
- return assess.dataflow.sources.bodyParser1Instrumentation;
137
+ return core.assess.dataflow.sources.bodyParser1Instrumentation;
131
138
  };
@@ -16,10 +16,11 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../constants');
19
20
  const { patchType } = require('../common');
20
21
 
21
22
  module.exports = function init(core) {
22
- const { assess, depHooks, logger, patcher, scopes } = core;
23
+ const { assess, depHooks, logger, patcher } = core;
23
24
 
24
25
  assess.dataflow.sources.cookieParser1Instrumentation = {
25
26
  install() {
@@ -39,11 +40,11 @@ module.exports = function init(core) {
39
40
  const { funcKey } = data;
40
41
  const [req, , next] = data.args;
41
42
  data.args[2] = function contrastNext(...args) {
42
- const sourceContext = scopes.sources.getStore()?.assess;
43
+ const sourceContext = assess.getSourceContext(SOURCE);
43
44
 
44
45
  if (!sourceContext) {
45
46
  logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
46
- return;
47
+ return next(...args);
47
48
  }
48
49
 
49
50
  if (sourceContext.parsedCookies) {
@@ -15,17 +15,19 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { InputType } = require('@contrast/common');
18
+ const { InputType: { QUERYSTRING: inputType } } = require('@contrast/common');
19
19
  const { patchType } = require('../common');
20
-
21
- const inputType = InputType.QUERYSTRING;
20
+ const { InstrumentationType: { SOURCE } } = require('../../../constants');
22
21
 
23
22
  module.exports = (core) => {
24
23
  const {
25
24
  depHooks,
26
25
  patcher,
27
26
  logger,
28
- assess: { dataflow: { sources } },
27
+ assess: {
28
+ getSourceContext,
29
+ dataflow: { sources }
30
+ },
29
31
  } = core;
30
32
 
31
33
  // Patch `qs`
@@ -36,7 +38,7 @@ module.exports = (core) => {
36
38
  name,
37
39
  patchType,
38
40
  post({ args, hooked, orig, result, funcKey }) {
39
- const sourceContext = core.scopes.sources.getStore()?.assess;
41
+ const sourceContext = getSourceContext(SOURCE);
40
42
 
41
43
  if (!sourceContext) {
42
44
  logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
@@ -17,9 +17,15 @@
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
19
  const { patchType } = require('../common');
20
+ const { InstrumentationType: { SOURCE } } = require('../../../constants');
20
21
 
21
22
  module.exports = (core) => {
22
- const { depHooks, patcher, logger } = core;
23
+ const {
24
+ assess: { getSourceContext },
25
+ depHooks,
26
+ patcher,
27
+ logger,
28
+ } = core;
23
29
 
24
30
  core.assess.dataflow.sources.querystringInstrumentation = {
25
31
  install() {
@@ -29,7 +35,7 @@ module.exports = (core) => {
29
35
  name,
30
36
  patchType,
31
37
  post({ args, hooked, orig, result, funcKey }) {
32
- const sourceContext = core.scopes.sources.getStore()?.assess;
38
+ const sourceContext = getSourceContext(SOURCE);
33
39
  const inputType = InputType.QUERYSTRING;
34
40
 
35
41
  if (!sourceContext) return;
@@ -14,6 +14,8 @@
14
14
  */
15
15
  'use strict';
16
16
 
17
+ const { split } = require('@contrast/common');
18
+
17
19
  //
18
20
  // This module implements tag range manipulation functions. There are generally
19
21
  // two types of functions:
@@ -461,8 +463,7 @@ function createAdjustedQueryTags(path, tags, value, argString) {
461
463
  break;
462
464
  }
463
465
  }
464
-
465
- return idx >= 0 ? createAppendTags([], tags, idx) : [...tags];
466
+ return idx >= 0 ? createAppendTags([], tags, idx) : { ...tags };
466
467
  }
467
468
 
468
469
  /**
@@ -484,8 +485,8 @@ function createAdjustedQueryTags(path, tags, value, argString) {
484
485
  * - "?test=str","%3Ftest%3Dstr",{"UNTRUSTED":[0,8]} => {"UNTRUSTED":[3,6,10,12]}
485
486
  */
486
487
  function createEscapeTagRanges(input, result, tags) {
487
- const inputArr = input.split('');
488
- const escapedArr = result.split('');
488
+ const inputArr = split(input, '');
489
+ const escapedArr = split(result, '');
489
490
  const overlap = inputArr.filter((x) => {
490
491
  if (escapedArr.includes(x)) {
491
492
  return x;
@@ -519,6 +520,21 @@ function createEscapeTagRanges(input, result, tags) {
519
520
  return ret;
520
521
  }
521
522
 
523
+ /**
524
+ * In reporting args, object, and return values, often the exact value isn't important.
525
+ * For untracked values that appear in call contexts it can be enough to just try to
526
+ * report the type of the arg/obj/result.
527
+ * Example: the call
528
+ * http.request('http://tracked-url', { method: 'post' });
529
+ * would have event context string limited to
530
+ * http.request('http://tracked-url,Object);
531
+ *
532
+ * @param {any} origValue value of event result, object, or arg
533
+ * @returns {string} the adjusted value for reporting
534
+ */
535
+ function getAdjustedUntrackedValue(origValue) {
536
+ return origValue?.constructor?.name ?? (origValue === null ? 'null' : typeof arg);
537
+ }
522
538
 
523
539
  module.exports = {
524
540
  createSubsetTags,
@@ -528,5 +544,6 @@ module.exports = {
528
544
  createTagsWithExclusion,
529
545
  createAdjustedQueryTags,
530
546
  createOverlappingTags,
531
- createEscapeTagRanges
547
+ createEscapeTagRanges,
548
+ getAdjustedUntrackedValue,
532
549
  };
@@ -39,7 +39,7 @@ module.exports = function(core) {
39
39
  const ctx = sources.getStore()?.assess;
40
40
 
41
41
  // policy will not exist if assess is altogether disabled for the active request e.g. url exclusion
42
- if (!ctx || !ctx?.policy || instrumentation.isLocked()) return null;
42
+ if (!ctx?.policy || instrumentation.isLocked()) return null;
43
43
 
44
44
  switch (type) {
45
45
  case InstrumentationType.PROPAGATOR: {
@@ -50,6 +50,7 @@ module.exports = function(core) {
50
50
  if (ctx.sourceEventsCount > config.assess.max_context_source_events) return null;
51
51
  break;
52
52
  }
53
+
53
54
  case InstrumentationType.RULE: {
54
55
  const [ruleId] = rest;
55
56
  if (!ruleId) break;