@contrast/assess 1.17.0 → 1.19.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 (53) hide show
  1. package/lib/constants.js +26 -0
  2. package/lib/crypto-analysis/common.js +20 -0
  3. package/lib/crypto-analysis/index.js +44 -0
  4. package/lib/crypto-analysis/install/crypto.js +151 -0
  5. package/lib/crypto-analysis/install/math.js +99 -0
  6. package/lib/dataflow/propagation/index.js +2 -1
  7. package/lib/dataflow/propagation/install/JSON/parse.js +12 -11
  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/ejs/escape-xml.js +2 -2
  11. package/lib/dataflow/propagation/install/ejs/index.js +1 -0
  12. package/lib/dataflow/propagation/install/ejs/template.js +77 -0
  13. package/lib/dataflow/propagation/install/sequelize/index.js +31 -0
  14. package/lib/dataflow/propagation/install/sequelize/query-generator.js +90 -0
  15. package/lib/dataflow/propagation/install/{sequelize.js → sequelize/sql-string.js} +3 -3
  16. package/lib/dataflow/propagation/install/util-format.js +126 -0
  17. package/lib/dataflow/sinks/index.js +1 -0
  18. package/lib/dataflow/sinks/install/child-process.js +20 -14
  19. package/lib/dataflow/sinks/install/eval.js +16 -14
  20. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +14 -8
  21. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +12 -5
  22. package/lib/dataflow/sinks/install/fs.js +7 -7
  23. package/lib/dataflow/sinks/install/function.js +8 -12
  24. package/lib/dataflow/sinks/install/http/request.js +16 -8
  25. package/lib/dataflow/sinks/install/http/server-response.js +11 -2
  26. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +15 -8
  27. package/lib/dataflow/sinks/install/libxmljs.js +15 -10
  28. package/lib/dataflow/sinks/install/marsdb.js +13 -8
  29. package/lib/dataflow/sinks/install/mongodb.js +25 -15
  30. package/lib/dataflow/sinks/install/mssql.js +20 -9
  31. package/lib/dataflow/sinks/install/mysql.js +15 -8
  32. package/lib/dataflow/sinks/install/node-serialize.js +101 -0
  33. package/lib/dataflow/sinks/install/postgres.js +17 -4
  34. package/lib/dataflow/sinks/install/sequelize.js +16 -9
  35. package/lib/dataflow/sinks/install/sqlite3.js +20 -7
  36. package/lib/dataflow/sinks/install/vm.js +19 -17
  37. package/lib/dataflow/sources/install/http.js +14 -42
  38. package/lib/dataflow/sources/install/koa/index.js +1 -0
  39. package/lib/dataflow/sources/install/koa/koa-multer.js +102 -0
  40. package/lib/dataflow/sources/install/multer1.js +25 -51
  41. package/lib/dataflow/sources/install/querystring.js +1 -4
  42. package/lib/event-factory.js +47 -0
  43. package/lib/get-policy.js +68 -0
  44. package/lib/get-source-context.js +62 -0
  45. package/lib/index.d.ts +50 -0
  46. package/lib/index.js +20 -19
  47. package/lib/make-source-context.js +74 -0
  48. package/lib/response-scanning/handlers/index.js +55 -28
  49. package/lib/response-scanning/install/http.js +13 -7
  50. package/lib/rule-scopes.js +48 -0
  51. package/lib/session-configuration/handlers.js +4 -3
  52. package/lib/session-configuration/install/express-session.js +8 -2
  53. package/package.json +2 -2
@@ -0,0 +1,126 @@
1
+ /*
2
+ * Copyright: 2023 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 { isString } = require('@contrast/common');
20
+ const { createAppendTags } = require('../../tag-utils');
21
+
22
+ module.exports = function(core) {
23
+ const {
24
+ scopes: { sources, instrumentation },
25
+ patcher,
26
+ depHooks,
27
+ assess: {
28
+ eventFactory: { createPropagationEvent },
29
+ dataflow: { tracker }
30
+ }
31
+ } = core;
32
+
33
+ return core.assess.dataflow.propagation.utilFormat = {
34
+ install() {
35
+ depHooks.resolve({ name: 'util' }, (util) => {
36
+ const name = 'util.format';
37
+
38
+ return patcher.patch(util, 'format', {
39
+ name,
40
+ patchType,
41
+ post(data) {
42
+ const { args, result, hooked, orig } = data;
43
+ if (!result || !args[0] || !isString(args[0]) || !sources.getStore()?.assess || instrumentation.isLocked()) return;
44
+
45
+ let idx = 0;
46
+ let newTags = {};
47
+ const history = [];
48
+ const eventArgs = [];
49
+ const formatChars = args[0].includes('%') ? args[0].match(/[^%]+/g).map((x) => x[0]) : [];
50
+ let i = 0;
51
+
52
+ if (formatChars.length > 0) {
53
+ i = 1;
54
+ eventArgs.push({ value: args[0], tracked: false });
55
+ }
56
+
57
+ for (i; i < args.length; i++) {
58
+ let arg = args[i];
59
+ const formatChar = formatChars[i - 1];
60
+ if (typeof arg === 'object') {
61
+
62
+ if (formatChar === 'j') {
63
+ arg = JSON.stringify(arg);
64
+ } else if (formatChar.toLowerCase() === 'o') {
65
+ // TO-DO: NODE-3235
66
+ }
67
+ }
68
+ const currIdx = result.indexOf(arg, idx);
69
+ if (currIdx > -1) {
70
+ idx = currIdx + arg.length;
71
+ } else {
72
+ continue;
73
+ }
74
+
75
+ const argInfo = tracker.getData(arg);
76
+ if (!argInfo) continue;
77
+
78
+ newTags = createAppendTags(newTags, argInfo.tags, currIdx);
79
+
80
+ history.push({ ...argInfo });
81
+ eventArgs.push({ value: argInfo ? argInfo.value : arg, tracked: !!argInfo });
82
+ }
83
+
84
+ const resultInfo = tracker.getData(result);
85
+ const event = createPropagationEvent({
86
+ name,
87
+ moduleName: 'util',
88
+ methodName: 'format',
89
+ context: `util.format(${eventArgs.map((arg) => `'${arg.value}'`)})`,
90
+ object: {
91
+ value: 'util',
92
+ tracked: false
93
+ },
94
+ result: {
95
+ value: resultInfo ? resultInfo.value : result,
96
+ tracked: true
97
+ },
98
+ args: eventArgs,
99
+ tags: newTags,
100
+ history,
101
+ source: 'P',
102
+ target: 'R',
103
+ stacktraceOpts: {
104
+ constructorOpt: hooked,
105
+ prependFrames: [orig]
106
+ },
107
+ });
108
+
109
+ if (!event) return;
110
+
111
+ if (resultInfo) {
112
+ Object.assign(resultInfo, event);
113
+ } else {
114
+ const { extern } = tracker.track(result, event);
115
+
116
+ if (extern) {
117
+ data.result = extern;
118
+ }
119
+ }
120
+ }
121
+ });
122
+ }
123
+ );
124
+ },
125
+ };
126
+ };
@@ -79,6 +79,7 @@ module.exports = function (core) {
79
79
  require('./install/mongodb')(core);
80
80
  require('./install/mssql')(core);
81
81
  require('./install/mysql')(core);
82
+ require('./install/node-serialize')(core);
82
83
  require('./install/postgres')(core);
83
84
  require('./install/sequelize')(core);
84
85
  require('./install/sqlite3')(core);
@@ -17,19 +17,25 @@
17
17
  const {
18
18
  DataflowTag: { UNTRUSTED },
19
19
  join,
20
- Rule,
20
+ Rule: { CMD_INJECTION: ruleId },
21
21
  isString,
22
22
  inspect,
23
23
  } = require('@contrast/common');
24
-
24
+ const { InstrumentationType: { RULE } } = require('../../../constants');
25
25
  const { patchType } = require('../common');
26
26
 
27
+ /**
28
+ * @param {{
29
+ * assess: import('@contrast/assess').Assess,
30
+ * }} core
31
+ * @returns {import('@contrast/common').Installable}
32
+ */
27
33
  module.exports = function(core) {
28
34
  const {
29
35
  depHooks,
30
36
  patcher,
31
- scopes: { sources },
32
37
  assess: {
38
+ getSourceContext,
33
39
  eventFactory: { createSinkEvent },
34
40
  dataflow: {
35
41
  tracker,
@@ -92,7 +98,7 @@ module.exports = function(core) {
92
98
 
93
99
  if (event) {
94
100
  reportFindings({
95
- ruleId: Rule.CMD_INJECTION,
101
+ ruleId,
96
102
  sinkEvent: event,
97
103
  });
98
104
 
@@ -178,7 +184,7 @@ module.exports = function(core) {
178
184
 
179
185
  if (event) {
180
186
  reportFindings({
181
- ruleId: Rule.CMD_INJECTION,
187
+ ruleId,
182
188
  sinkEvent: event,
183
189
  });
184
190
  }
@@ -193,10 +199,10 @@ module.exports = function(core) {
193
199
  name,
194
200
  patchType,
195
201
  pre(data) {
196
- const store = sources.getStore()?.assess;
197
- const [command] = data.args;
202
+ if (!getSourceContext(RULE, ruleId)) return;
198
203
 
199
- if (!store || !command || !isString(command)) return;
204
+ const [command] = data.args;
205
+ if (!command || !isString(command)) return;
200
206
 
201
207
  const cpArgs = Array.isArray(data.args[1]) && data.args[1];
202
208
  const options = cpArgs ? data.args[2] : data.args[1];
@@ -233,10 +239,10 @@ module.exports = function(core) {
233
239
  name,
234
240
  patchType,
235
241
  pre(data) {
236
- const store = sources.getStore()?.assess;
237
- const [command, secondArg, thirdArg] = data.args;
242
+ if (!getSourceContext(RULE, ruleId)) return;
238
243
 
239
- if (!store || !command || !isString(command)) return;
244
+ const [command, secondArg, thirdArg] = data.args;
245
+ if (!command || !isString(command)) return;
240
246
 
241
247
  commandCheck(
242
248
  name,
@@ -257,10 +263,10 @@ module.exports = function(core) {
257
263
  name,
258
264
  patchType,
259
265
  pre(data) {
260
- const store = sources.getStore()?.assess;
261
- const [command] = data.args;
266
+ if (!getSourceContext(RULE, ruleId)) return;
262
267
 
263
- if (!store || !command || !isString(command)) return;
268
+ const [command] = data.args;
269
+ if (!command || !isString(command)) return;
264
270
 
265
271
  const cpArgs = Array.isArray(data.args[1]) && data.args[1];
266
272
  const options = cpArgs ? data.args[2] : data.args[1];
@@ -25,8 +25,9 @@ const {
25
25
  CUSTOM_VALIDATED,
26
26
  LIMITED_CHARS,
27
27
  },
28
- Rule: { UNSAFE_CODE_EXECUTION },
28
+ Rule: { UNSAFE_CODE_EXECUTION: ruleId },
29
29
  } = require('@contrast/common');
30
+ const { InstrumentationType: { RULE } } = require('../../../constants');
30
31
  const { patchType, filterSafeTags } = require('../common');
31
32
 
32
33
  const safeTags = [
@@ -37,13 +38,21 @@ const safeTags = [
37
38
  LIMITED_CHARS,
38
39
  ];
39
40
 
41
+ /**
42
+ * @param {{
43
+ * assess: import('@contrast/assess').Assess,
44
+ * config: import('@contrast/config').Config,
45
+ * logger: import('@contrast/logger').Logger,
46
+ * }} core
47
+ * @returns {import('@contrast/common').Installable}
48
+ */
40
49
  module.exports = function (core) {
41
50
  const {
42
51
  config,
43
52
  logger,
44
53
  patcher,
45
- scopes: { sources, instrumentation },
46
54
  assess: {
55
+ getSourceContext,
47
56
  eventFactory: { createSinkEvent },
48
57
  dataflow: {
49
58
  tracker,
@@ -63,19 +72,12 @@ module.exports = function (core) {
63
72
  name: 'global.ContrastMethods.eval',
64
73
  patchType,
65
74
  pre({ args, orig }) {
66
- const store = sources.getStore()?.assess;
75
+ if (!getSourceContext(RULE, ruleId)) return;
76
+
67
77
  const script = args[0];
68
- if (
69
- !store ||
70
- instrumentation.isLocked() ||
71
- !script ||
72
- !isString(script)
73
- ) {
74
- return;
75
- }
78
+ if (!script || !isString(script)) return;
76
79
 
77
80
  const strInfo = tracker.getData(script);
78
-
79
81
  if (!strInfo) return;
80
82
 
81
83
  const isArgVulnerable = isVulnerable(
@@ -93,7 +95,7 @@ module.exports = function (core) {
93
95
 
94
96
  reportSafePositive({
95
97
  name: 'eval',
96
- ruleId: UNSAFE_CODE_EXECUTION,
98
+ ruleId,
97
99
  safeTags: foundSafeTags,
98
100
  strInfo: safeStrInfo,
99
101
  });
@@ -120,7 +122,7 @@ module.exports = function (core) {
120
122
 
121
123
  if (event) {
122
124
  reportFindings({
123
- ruleId: UNSAFE_CODE_EXECUTION,
125
+ ruleId,
124
126
  sinkEvent: event,
125
127
  });
126
128
  }
@@ -17,6 +17,7 @@
17
17
 
18
18
  const util = require('util');
19
19
  const {
20
+ Rule: { UNVALIDATED_REDIRECT: ruleId },
20
21
  DataflowTag: {
21
22
  UNTRUSTED,
22
23
  CUSTOM_ENCODED,
@@ -27,18 +28,25 @@ const {
27
28
  },
28
29
  isString
29
30
  } = require('@contrast/common');
30
- const { patchType, filterSafeTags } = require('../../common');
31
+ const { InstrumentationType: { RULE } } = require('../../../../constants');
31
32
  const { createSubsetTags } = require('../../../tag-utils');
33
+ const { patchType, filterSafeTags } = require('../../common');
32
34
 
33
- const ruleId = 'unvalidated-redirect';
34
-
35
+ /**
36
+ * @param {{
37
+ * assess: import('@contrast/assess').Assess,
38
+ * config: import('@contrast/config').Config,
39
+ * logger: import('@contrast/logger').Logger,
40
+ * }} core
41
+ * @returns {import('@contrast/common').Installable}
42
+ */
35
43
  module.exports = function(core) {
36
44
  const {
37
45
  depHooks,
38
46
  patcher,
39
47
  config,
40
- scopes: { sources },
41
48
  assess: {
49
+ getSourceContext,
42
50
  eventFactory: { createSinkEvent },
43
51
  dataflow: {
44
52
  tracker,
@@ -46,9 +54,8 @@ module.exports = function(core) {
46
54
  },
47
55
  },
48
56
  } = core;
49
- const unvalidatedRedirect =
50
- (core.assess.dataflow.sinks.express.unvalidatedRedirect = {});
51
57
 
58
+ const unvalidatedRedirect = core.assess.dataflow.sinks.express.unvalidatedRedirect = {};
52
59
  const inspect = patcher.unwrap(util.inspect);
53
60
 
54
61
  const safeTags = [
@@ -66,8 +73,7 @@ module.exports = function(core) {
66
73
  name: 'Express.Response.location',
67
74
  patchType,
68
75
  pre: (data) => {
69
- const assessStore = sources.getStore()?.assess;
70
- if (!assessStore) return;
76
+ if (!getSourceContext(RULE, ruleId)) return;
71
77
 
72
78
  let [url] = data.args;
73
79
  if (url === 'back') {
@@ -17,6 +17,7 @@
17
17
 
18
18
  const util = require('util');
19
19
  const {
20
+ Rule: { UNVALIDATED_REDIRECT: ruleId },
20
21
  DataflowTag: {
21
22
  UNTRUSTED,
22
23
  CUSTOM_ENCODED,
@@ -27,11 +28,10 @@ const {
27
28
  },
28
29
  isString
29
30
  } = require('@contrast/common');
31
+ const { InstrumentationType: { RULE } } = require('../../../../constants');
30
32
  const { createSubsetTags } = require('../../../tag-utils');
31
33
  const { filterSafeTags, patchType } = require('../../common');
32
34
 
33
- const ruleId = 'unvalidated-redirect';
34
-
35
35
  const getURLArgument = (args) => {
36
36
  if (!Array.isArray(args)) {
37
37
  return { index: null, url: undefined };
@@ -51,13 +51,21 @@ const getURLArgument = (args) => {
51
51
  };
52
52
  };
53
53
 
54
+ /**
55
+ *
56
+ * @param {{
57
+ * assess: import('@contrast/assess').Assess,
58
+ * config: import('@contrast/config').Config,
59
+ * }} core
60
+ * @returns {import('@contrast/common').Installable}
61
+ */
54
62
  module.exports = function(core) {
55
63
  const {
56
64
  config,
57
65
  depHooks,
58
66
  patcher,
59
- scopes: { sources },
60
67
  assess: {
68
+ getSourceContext,
61
69
  eventFactory: { createSinkEvent },
62
70
  dataflow: {
63
71
  tracker,
@@ -85,8 +93,7 @@ module.exports = function(core) {
85
93
  name,
86
94
  patchType,
87
95
  post(data) {
88
- const assessStore = sources.getStore()?.assess;
89
- if (!assessStore) return;
96
+ if (!getSourceContext(RULE, ruleId)) return;
90
97
 
91
98
  const { url, index: valueIndex } = getURLArgument(data.args);
92
99
  if (!url || !isString(url)) return;
@@ -16,9 +16,6 @@
16
16
  'use strict';
17
17
  const { patchType } = require('../common');
18
18
  const {
19
- FS_METHODS,
20
- Rule,
21
- isString,
22
19
  DataflowTag: {
23
20
  URL_ENCODED,
24
21
  LIMITED_CHARS,
@@ -26,16 +23,20 @@ const {
26
23
  SAFE_PATH,
27
24
  UNTRUSTED,
28
25
  },
26
+ FS_METHODS,
27
+ Rule: { PATH_TRAVERSAL: ruleId },
29
28
  inspect,
29
+ isString,
30
30
  join,
31
31
  } = require('@contrast/common');
32
+ const { InstrumentationType: { RULE } } = require('../../../constants');
32
33
 
33
34
  module.exports = function(core) {
34
35
  const {
35
36
  depHooks,
36
37
  patcher,
37
- scopes: { instrumentation, sources },
38
38
  assess: {
39
+ getSourceContext,
39
40
  eventFactory: { createSinkEvent },
40
41
  dataflow: {
41
42
  tracker,
@@ -61,8 +62,7 @@ module.exports = function(core) {
61
62
 
62
63
  const pre = (name, method, moduleName = 'fs', fullMethodName = '') => (data) => {
63
64
  const { name: methodName, indices } = method;
64
- const store = sources.getStore()?.assess;
65
- if (!store || instrumentation.isLocked()) return;
65
+ if (!getSourceContext(RULE, ruleId)) return;
66
66
 
67
67
  const values = getValues(indices, data.args);
68
68
  if (!values.length) return;
@@ -105,7 +105,7 @@ module.exports = function(core) {
105
105
 
106
106
  if (event) {
107
107
  reportFindings({
108
- ruleId: Rule.PATH_TRAVERSAL,
108
+ ruleId,
109
109
  sinkEvent: event,
110
110
  });
111
111
  }
@@ -27,8 +27,9 @@ const {
27
27
  CUSTOM_VALIDATED,
28
28
  LIMITED_CHARS,
29
29
  },
30
- Rule: { UNSAFE_CODE_EXECUTION }
30
+ Rule: { UNSAFE_CODE_EXECUTION: ruleId }
31
31
  } = require('@contrast/common');
32
+ const { InstrumentationType: { RULE } } = require('../../../constants');
32
33
  const { patchType, filterSafeTags } = require('../common');
33
34
 
34
35
  const safeTags = [
@@ -44,8 +45,8 @@ module.exports = function (core) {
44
45
  config,
45
46
  logger,
46
47
  patcher,
47
- scopes: { sources, instrumentation },
48
48
  assess: {
49
+ getSourceContext,
49
50
  eventFactory: { createSinkEvent },
50
51
  dataflow: {
51
52
  tracker,
@@ -65,15 +66,10 @@ module.exports = function (core) {
65
66
  name: 'global.ContrastMethods.Function',
66
67
  patchType,
67
68
  pre({ args: origArgs, hooked, orig, name }) {
68
- const store = sources.getStore()?.assess;
69
+ if (!getSourceContext(RULE, ruleId)) return;
70
+
69
71
  const fnBody = origArgs[origArgs.length - 1];
70
- if (
71
- !store ||
72
- instrumentation.isLocked() ||
73
- !fnBody ||
74
- !isString(fnBody)
75
- )
76
- return;
72
+ if (!fnBody || !isString(fnBody)) return;
77
73
 
78
74
  const strInfo = tracker.getData(fnBody);
79
75
 
@@ -90,7 +86,7 @@ module.exports = function (core) {
90
86
 
91
87
  reportSafePositive({
92
88
  name,
93
- ruleId: UNSAFE_CODE_EXECUTION,
89
+ ruleId,
94
90
  safeTags: foundSafeTags,
95
91
  strInfo: safeStrInfo,
96
92
  });
@@ -142,7 +138,7 @@ module.exports = function (core) {
142
138
 
143
139
  if (event) {
144
140
  reportFindings({
145
- ruleId: UNSAFE_CODE_EXECUTION,
141
+ ruleId,
146
142
  sinkEvent: event,
147
143
  });
148
144
  }
@@ -15,6 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
+ const Url = require('url');
18
19
  const {
19
20
  inspect,
20
21
  isString,
@@ -25,19 +26,27 @@ const {
25
26
  CUSTOM_VALIDATED_SSRF,
26
27
  CUSTOM_VALIDATED,
27
28
  LIMITED_CHARS
28
- }
29
+ },
30
+ Rule: { SSRF: ruleId },
29
31
  } = require('@contrast/common');
30
- const Url = require('url');
31
- const trustedLibs = [/^(?!.*(newrelic)).*http.*$/];
32
- const { patchType } = require('../../common');
32
+ const { InstrumentationType: { RULE } } = require('../../../../constants');
33
33
  const { createAppendTags } = require('../../../tag-utils');
34
+ const { patchType } = require('../../common');
35
+
36
+ const trustedLibs = [/^(?!.*(newrelic)).*http.*$/];
34
37
 
38
+ /**
39
+ * @param {{
40
+ * assess: import('@contrast/assess').Assess,
41
+ * }} core
42
+ * @returns {import('@contrast/common').Installable}
43
+ */
35
44
  module.exports = function(core) {
36
45
  const {
37
46
  depHooks,
38
47
  patcher,
39
- scopes: { sources },
40
48
  assess: {
49
+ getSourceContext,
41
50
  eventFactory: { createSinkEvent },
42
51
  dataflow: {
43
52
  tracker,
@@ -92,8 +101,7 @@ module.exports = function(core) {
92
101
  name,
93
102
  patchType,
94
103
  pre(data) {
95
- const sourceContext = sources.getStore()?.assess;
96
- if (!sourceContext) return;
104
+ if (!getSourceContext(RULE, ruleId)) return;
97
105
 
98
106
  const [req] = data.args;
99
107
  if (!req) return;
@@ -136,7 +144,7 @@ module.exports = function(core) {
136
144
 
137
145
  if (event) {
138
146
  reportFindings({
139
- ruleId: 'ssrf',
147
+ ruleId,
140
148
  sinkEvent: event
141
149
  });
142
150
  }
@@ -31,15 +31,24 @@ const {
31
31
  },
32
32
  Rule: { REFLECTED_XSS: ruleId },
33
33
  } = require('@contrast/common');
34
+ const { InstrumentationType: { RULE } } = require('../../../../constants');
35
+
34
36
  const { patchType, filterSafeTags } = require('../../common');
35
37
 
38
+ /**
39
+ * @param {{
40
+ * assess: import('@contrast/assess').Assess,
41
+ * config: import('@contrast/config').Config,
42
+ * }} core
43
+ * @returns {import('@contrast/common').Installable}
44
+ */
36
45
  module.exports = function(core) {
37
46
  const {
38
47
  config,
39
48
  depHooks,
40
49
  patcher,
41
- scopes: { sources },
42
50
  assess: {
51
+ getSourceContext,
43
52
  eventFactory: { createSinkEvent },
44
53
  dataflow: {
45
54
  tracker,
@@ -68,7 +77,7 @@ module.exports = function(core) {
68
77
  ];
69
78
 
70
79
  const preHook = (name, method) => (data) => {
71
- const sourceContext = sources.getStore()?.assess;
80
+ const sourceContext = getSourceContext(RULE, ruleId);
72
81
  if (!sourceContext) return;
73
82
 
74
83
  const payload = data.args[0];
@@ -25,20 +25,29 @@ const {
25
25
  LIMITED_CHARS,
26
26
  URL_ENCODED,
27
27
  },
28
+ Rule: { UNVALIDATED_REDIRECT: ruleId },
28
29
  isString
29
30
  } = require('@contrast/common');
31
+ const { InstrumentationType: { RULE } } = require('../../../../constants');
30
32
  const { createSubsetTags } = require('../../../tag-utils');
31
33
  const { filterSafeTags, patchType } = require('../../common');
32
34
 
33
- const ruleId = 'unvalidated-redirect';
34
-
35
+ /**
36
+ * @param {{
37
+ * assess: import('@contrast/assess').Assess,
38
+ * config: import('@contrast/config').Config,
39
+ * logger: import('@contrast/logger').Logger,
40
+ * messages: import('@contrast/common').Messages,
41
+ * }} core
42
+ * @returns {import('@contrast/common').Installable}
43
+ */
35
44
  module.exports = function(core) {
36
45
  const {
37
46
  depHooks,
38
47
  patcher,
39
48
  config,
40
- scopes: { sources },
41
49
  assess: {
50
+ getSourceContext,
42
51
  eventFactory: { createSinkEvent },
43
52
  dataflow: {
44
53
  tracker,
@@ -46,11 +55,8 @@ module.exports = function(core) {
46
55
  },
47
56
  },
48
57
  } = core;
49
- const unvalidatedRedirect =
50
- (core.assess.dataflow.sinks.koa.unvalidatedRedirect = {});
51
58
 
52
59
  const inspect = patcher.unwrap(util.inspect);
53
-
54
60
  const safeTags = [
55
61
  CUSTOM_ENCODED,
56
62
  CUSTOM_VALIDATED,
@@ -59,6 +65,8 @@ module.exports = function(core) {
59
65
  URL_ENCODED,
60
66
  ];
61
67
 
68
+ const unvalidatedRedirect = core.assess.dataflow.sinks.koa.unvalidatedRedirect = {};
69
+
62
70
  unvalidatedRedirect.install = function() {
63
71
  depHooks.resolve({ name: 'koa', file: 'lib/response', version: '<2.9.0' }, (Response) => {
64
72
  const name = 'Koa.Response.redirect';
@@ -66,8 +74,7 @@ module.exports = function(core) {
66
74
  name,
67
75
  patchType,
68
76
  pre(data) {
69
- const assessStore = sources.getStore()?.assess;
70
- if (!assessStore) return;
77
+ if (!getSourceContext(RULE, ruleId)) return;
71
78
 
72
79
  let isBackRoute = false;
73
80
  let [url] = data.args;