@contrast/assess 1.18.0 → 1.20.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 (160) hide show
  1. package/LICENSE +1 -1
  2. package/lib/constants.js +26 -0
  3. package/lib/crypto-analysis/common.js +20 -0
  4. package/lib/crypto-analysis/index.js +44 -0
  5. package/lib/crypto-analysis/install/crypto.js +156 -0
  6. package/lib/crypto-analysis/install/math.js +104 -0
  7. package/lib/dataflow/index.js +1 -1
  8. package/lib/dataflow/propagation/common.js +1 -1
  9. package/lib/dataflow/propagation/index.js +1 -1
  10. package/lib/dataflow/propagation/install/JSON/index.js +1 -1
  11. package/lib/dataflow/propagation/install/JSON/parse-fn.js +1 -1
  12. package/lib/dataflow/propagation/install/JSON/parse.js +15 -14
  13. package/lib/dataflow/propagation/install/JSON/stringify.js +2 -2
  14. package/lib/dataflow/propagation/install/array-prototype-join.js +1 -1
  15. package/lib/dataflow/propagation/install/buffer.js +1 -1
  16. package/lib/dataflow/propagation/install/contrast-methods/add.js +1 -1
  17. package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -1
  18. package/lib/dataflow/propagation/install/contrast-methods/number.js +4 -3
  19. package/lib/dataflow/propagation/install/contrast-methods/string.js +1 -1
  20. package/lib/dataflow/propagation/install/contrast-methods/tag.js +1 -1
  21. package/lib/dataflow/propagation/install/decode-uri-component.js +1 -1
  22. package/lib/dataflow/propagation/install/ejs/escape-xml.js +3 -3
  23. package/lib/dataflow/propagation/install/ejs/index.js +2 -1
  24. package/lib/dataflow/propagation/install/ejs/template.js +79 -0
  25. package/lib/dataflow/propagation/install/encode-uri.js +1 -1
  26. package/lib/dataflow/propagation/install/escape-html.js +1 -1
  27. package/lib/dataflow/propagation/install/escape.js +1 -1
  28. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +1 -1
  29. package/lib/dataflow/propagation/install/isnumeric-0.js +3 -3
  30. package/lib/dataflow/propagation/install/joi/any.js +1 -1
  31. package/lib/dataflow/propagation/install/joi/boolean.js +1 -1
  32. package/lib/dataflow/propagation/install/joi/expression.js +1 -1
  33. package/lib/dataflow/propagation/install/joi/index.js +1 -1
  34. package/lib/dataflow/propagation/install/joi/keys.js +1 -1
  35. package/lib/dataflow/propagation/install/joi/number.js +1 -1
  36. package/lib/dataflow/propagation/install/joi/object.js +1 -1
  37. package/lib/dataflow/propagation/install/joi/string-schema.js +1 -1
  38. package/lib/dataflow/propagation/install/joi/utils.js +1 -1
  39. package/lib/dataflow/propagation/install/joi/values.js +1 -1
  40. package/lib/dataflow/propagation/install/mongoose/common.js +1 -1
  41. package/lib/dataflow/propagation/install/mongoose/index.js +1 -1
  42. package/lib/dataflow/propagation/install/mongoose/schema-map.js +1 -1
  43. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +1 -1
  44. package/lib/dataflow/propagation/install/mongoose/schema-string.js +1 -1
  45. package/lib/dataflow/propagation/install/mustache-escape.js +1 -1
  46. package/lib/dataflow/propagation/install/mysql-connection-escape.js +1 -1
  47. package/lib/dataflow/propagation/install/parse-int.js +3 -3
  48. package/lib/dataflow/propagation/install/path/basename.js +1 -1
  49. package/lib/dataflow/propagation/install/path/common.js +1 -1
  50. package/lib/dataflow/propagation/install/path/dirname.js +1 -1
  51. package/lib/dataflow/propagation/install/path/extname.js +1 -1
  52. package/lib/dataflow/propagation/install/path/format.js +1 -1
  53. package/lib/dataflow/propagation/install/path/index.js +1 -1
  54. package/lib/dataflow/propagation/install/path/join-and-resolve.js +1 -1
  55. package/lib/dataflow/propagation/install/path/normalize.js +1 -1
  56. package/lib/dataflow/propagation/install/path/parse.js +1 -1
  57. package/lib/dataflow/propagation/install/path/relative.js +1 -1
  58. package/lib/dataflow/propagation/install/path/toNamespacedPath.js +1 -1
  59. package/lib/dataflow/propagation/install/pug/index.js +3 -3
  60. package/lib/dataflow/propagation/install/pug-runtime-escape.js +1 -1
  61. package/lib/dataflow/propagation/install/querystring/escape.js +1 -1
  62. package/lib/dataflow/propagation/install/querystring/index.js +1 -1
  63. package/lib/dataflow/propagation/install/querystring/parse.js +1 -1
  64. package/lib/dataflow/propagation/install/querystring/stringify.js +1 -1
  65. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +1 -1
  66. package/lib/dataflow/propagation/install/send.js +1 -1
  67. package/lib/dataflow/propagation/install/sequelize/index.js +1 -1
  68. package/lib/dataflow/propagation/install/sequelize/query-generator.js +1 -1
  69. package/lib/dataflow/propagation/install/sequelize/sql-string.js +1 -1
  70. package/lib/dataflow/propagation/install/sql-template-strings.js +1 -1
  71. package/lib/dataflow/propagation/install/string/concat.js +1 -1
  72. package/lib/dataflow/propagation/install/string/format-methods.js +1 -1
  73. package/lib/dataflow/propagation/install/string/html-methods.js +1 -1
  74. package/lib/dataflow/propagation/install/string/index.js +1 -1
  75. package/lib/dataflow/propagation/install/string/match-all.js +1 -1
  76. package/lib/dataflow/propagation/install/string/match.js +1 -1
  77. package/lib/dataflow/propagation/install/string/replace.js +1 -1
  78. package/lib/dataflow/propagation/install/string/slice.js +1 -1
  79. package/lib/dataflow/propagation/install/string/split.js +1 -1
  80. package/lib/dataflow/propagation/install/string/substring.js +1 -1
  81. package/lib/dataflow/propagation/install/string/trim.js +1 -1
  82. package/lib/dataflow/propagation/install/unescape.js +1 -1
  83. package/lib/dataflow/propagation/install/url/domain-parsers.js +1 -1
  84. package/lib/dataflow/propagation/install/url/index.js +1 -1
  85. package/lib/dataflow/propagation/install/url/parse.js +1 -1
  86. package/lib/dataflow/propagation/install/url/searchParams.js +1 -1
  87. package/lib/dataflow/propagation/install/url/url.js +1 -1
  88. package/lib/dataflow/propagation/install/util-format.js +10 -4
  89. package/lib/dataflow/propagation/install/validator/hooks.js +1 -1
  90. package/lib/dataflow/propagation/install/validator/index.js +1 -1
  91. package/lib/dataflow/propagation/install/validator/methods.js +1 -1
  92. package/lib/dataflow/sinks/common.js +1 -1
  93. package/lib/dataflow/sinks/index.js +1 -1
  94. package/lib/dataflow/sinks/install/child-process.js +21 -15
  95. package/lib/dataflow/sinks/install/eval.js +17 -15
  96. package/lib/dataflow/sinks/install/express/index.js +1 -1
  97. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +15 -9
  98. package/lib/dataflow/sinks/install/fastify/index.js +1 -1
  99. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +13 -6
  100. package/lib/dataflow/sinks/install/fs.js +8 -8
  101. package/lib/dataflow/sinks/install/function.js +9 -13
  102. package/lib/dataflow/sinks/install/http/index.js +1 -1
  103. package/lib/dataflow/sinks/install/http/request.js +17 -9
  104. package/lib/dataflow/sinks/install/http/server-response.js +12 -3
  105. package/lib/dataflow/sinks/install/koa/index.js +1 -1
  106. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +16 -9
  107. package/lib/dataflow/sinks/install/libxmljs.js +16 -11
  108. package/lib/dataflow/sinks/install/marsdb.js +17 -12
  109. package/lib/dataflow/sinks/install/mongodb.js +32 -22
  110. package/lib/dataflow/sinks/install/mssql.js +21 -10
  111. package/lib/dataflow/sinks/install/mysql.js +16 -9
  112. package/lib/dataflow/sinks/install/node-serialize.js +16 -18
  113. package/lib/dataflow/sinks/install/postgres.js +18 -5
  114. package/lib/dataflow/sinks/install/sequelize.js +23 -17
  115. package/lib/dataflow/sinks/install/sqlite3.js +21 -8
  116. package/lib/dataflow/sinks/install/vm.js +20 -18
  117. package/lib/dataflow/sources/common.js +1 -1
  118. package/lib/dataflow/sources/handler.js +11 -10
  119. package/lib/dataflow/sources/index.js +2 -2
  120. package/lib/dataflow/sources/install/body-parser1.js +11 -13
  121. package/lib/dataflow/sources/install/{busboy1.js → busboy.js} +15 -15
  122. package/lib/dataflow/sources/install/cookie-parser1.js +7 -6
  123. package/lib/dataflow/sources/install/express/index.js +1 -1
  124. package/lib/dataflow/sources/install/express/params.js +9 -10
  125. package/lib/dataflow/sources/install/express/parsedUrl.js +1 -1
  126. package/lib/dataflow/sources/install/fastify/fastify.js +6 -7
  127. package/lib/dataflow/sources/install/fastify/index.js +1 -1
  128. package/lib/dataflow/sources/install/formidable1.js +8 -6
  129. package/lib/dataflow/sources/install/http.js +17 -45
  130. package/lib/dataflow/sources/install/koa/index.js +2 -1
  131. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +10 -9
  132. package/lib/dataflow/sources/install/koa/koa-multer.js +102 -0
  133. package/lib/dataflow/sources/install/koa/koa-routers.js +6 -8
  134. package/lib/dataflow/sources/install/koa/koa2.js +42 -38
  135. package/lib/dataflow/sources/install/multer1.js +26 -52
  136. package/lib/dataflow/sources/install/qs6.js +7 -6
  137. package/lib/dataflow/sources/install/querystring.js +5 -8
  138. package/lib/dataflow/tag-utils.js +1 -1
  139. package/lib/dataflow/tracker.js +1 -1
  140. package/lib/dataflow/utils/is-safe-content-type.js +1 -1
  141. package/lib/dataflow/utils/is-vulnerable.js +1 -1
  142. package/lib/event-factory.js +75 -26
  143. package/lib/get-policy.js +68 -0
  144. package/lib/get-source-context.js +62 -0
  145. package/lib/index.d.ts +64 -0
  146. package/lib/index.js +21 -20
  147. package/lib/make-source-context.js +78 -0
  148. package/lib/response-scanning/handlers/index.js +56 -29
  149. package/lib/response-scanning/handlers/utils.js +1 -1
  150. package/lib/response-scanning/index.js +1 -1
  151. package/lib/response-scanning/install/http.js +14 -8
  152. package/lib/rule-scopes.js +48 -0
  153. package/lib/session-configuration/common.js +1 -1
  154. package/lib/session-configuration/handlers.js +66 -47
  155. package/lib/session-configuration/index.js +3 -1
  156. package/lib/session-configuration/install/express-session.js +23 -26
  157. package/lib/session-configuration/install/fastify-cookie.js +110 -0
  158. package/lib/session-configuration/install/hapi.js +8 -11
  159. package/lib/session-configuration/install/koa.js +101 -0
  160. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -18,6 +18,8 @@
18
18
  const { InputType } = require('@contrast/common');
19
19
  const { patchType } = require('../common');
20
20
 
21
+ const inputType = InputType.MULTIPART_VALUE;
22
+
21
23
  module.exports = (core) => {
22
24
  const {
23
25
  depHooks,
@@ -25,8 +27,8 @@ module.exports = (core) => {
25
27
  logger,
26
28
  assess: { dataflow: { sources } },
27
29
  } = core;
28
- const name = 'Formidable.IncomingForm.prototype.parse';
29
30
 
31
+ const name = 'Formidable.IncomingForm.prototype.parse';
30
32
 
31
33
  // Patch `formidable`
32
34
  function install() {
@@ -35,16 +37,16 @@ module.exports = (core) => {
35
37
  name,
36
38
  patchType,
37
39
  pre(data) {
40
+ const { funcKey } = data;
38
41
  const sourceContext = core.scopes.sources.getStore()?.assess;
39
- const inputType = InputType.MULTIPART_VALUE;
40
42
 
41
43
  if (!sourceContext) {
42
- logger.error({ inputType, name }, 'unable to handle source. Missing `sourceContext`');
44
+ logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
43
45
  return;
44
46
  }
45
47
 
46
48
  if (sourceContext.parsedBody) {
47
- logger.trace({ inputType, name }, 'values already tracked');
49
+ logger.trace({ inputType, funcKey }, 'values already tracked');
48
50
  return;
49
51
  }
50
52
 
@@ -68,7 +70,7 @@ module.exports = (core) => {
68
70
  });
69
71
  sourceContext.parsedBody = true;
70
72
  } catch (err) {
71
- logger.error({ err, inputType, name }, 'unable to handle source');
73
+ logger.error({ err, inputType, funcKey }, 'unable to handle source');
72
74
  }
73
75
  }
74
76
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -17,12 +17,17 @@
17
17
  const { patchType } = require('../common');
18
18
  const { toLowerCase, InputType } = require('@contrast/common');
19
19
 
20
- module.exports = function(core) {
20
+ /**
21
+ * @param {{
22
+ * assess: import('@contrast/assess').Assess,
23
+ * }} core
24
+ */
25
+ module.exports = function (core) {
21
26
  const {
22
- scopes,
27
+ assess: { dataflow, makeSourceContext },
23
28
  instrumentation: { instrument },
24
- assess: { dataflow },
25
29
  patcher,
30
+ scopes,
26
31
  } = core;
27
32
 
28
33
  const logger = core.logger.child('contrast:assess');
@@ -34,19 +39,20 @@ module.exports = function(core) {
34
39
  function around(next, data) {
35
40
  const [type] = data.args;
36
41
 
37
- if (type !== 'request') {
38
- return next();
39
- }
42
+ if (type !== 'request') return next();
40
43
 
41
44
  try {
42
45
  const [, req, res] = data.args;
43
46
  const store = scopes.sources.getStore();
44
47
 
45
48
  if (!store) {
46
- logger.debug('cannot acquire store for assess request handling');
47
- return next();
49
+ // this would indicate that sources did not install correctly
50
+ throw new Error('async request store not found');
48
51
  }
49
52
 
53
+ store.assess = makeSourceContext(req, res);
54
+ if (!store.assess) return;
55
+
50
56
  patcher.patch(res, 'writeHead', {
51
57
  name: 'write-head',
52
58
  patchType,
@@ -86,27 +92,7 @@ module.exports = function(core) {
86
92
  });
87
93
  }
88
94
 
89
- let uriPath, queries;
90
- const ix = req.url.indexOf('?');
91
-
92
- if (ix >= 0) {
93
- uriPath = req.url.slice(0, ix);
94
- queries = req.url.slice(ix + 1);
95
- } else {
96
- uriPath = req.url;
97
- queries = '';
98
- }
99
-
100
- const headers = {};
101
95
  const sourceName = 'ClientRequest';
102
-
103
- store.assess = {
104
- responseData: {},
105
- sourceEventsCount: 0,
106
- propagationEventsCount: 0,
107
- findings: {},
108
- };
109
-
110
96
  const sourceInfo = {
111
97
  name: sourceName,
112
98
  stacktraceOpts: {
@@ -135,30 +121,16 @@ module.exports = function(core) {
135
121
  try {
136
122
  dataflow.sources.handle(sourceData);
137
123
  } catch (err) {
138
- logger.error({ err, inputType, name: sourceName }, 'unable to handle http source');
124
+ logger.error({ err, inputType, sourceName }, 'unable to handle http source');
139
125
  }
140
126
  });
141
127
 
142
128
  for (let i = 0; i < req.rawHeaders.length; i += 2) {
143
129
  const header = toLowerCase(req.rawHeaders[i]);
144
- headers[header] = req.rawHeaders[i + 1];
145
130
  req.rawHeaders[i + 1] = req.headers[header];
146
131
  }
147
-
148
- const contentType = headers['content-type'] && toLowerCase(headers['content-type']);
149
-
150
- store.assess.reqData = {
151
- ip: req.socket.remoteAddress,
152
- httpVersion: req.httpVersion,
153
- method: req.method,
154
- headers,
155
- uriPath,
156
- queries,
157
- contentType,
158
- };
159
-
160
132
  } catch (err) {
161
- logger.error({ err }, 'Error during assess request handling');
133
+ logger.error({ err, funcKey: data.funcKey }, 'Error during Assess request handling');
162
134
  }
163
135
 
164
136
  setImmediate(() => {
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -22,6 +22,7 @@ module.exports = function(core) {
22
22
 
23
23
  require('./koa2')(core);
24
24
  require('./koa-bodyparsers')(core);
25
+ require('./koa-multer')(core);
25
26
  require('./koa-routers')(core);
26
27
 
27
28
  koaSources.install = function install() {
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -28,25 +28,26 @@ module.exports = (core) => {
28
28
 
29
29
  // Patch `koa-body` v4.x.x and `koa-bodyparser` v5.x.x packages
30
30
  function install() {
31
- ['koa-body', 'koa-bodyparser'].forEach(function (packageName) {
32
- depHooks.resolve({ name: packageName }, (koaBody) => patcher.patch(koaBody, {
33
- name: packageName,
31
+ ['koa-body', 'koa-bodyparser'].forEach((name) => {
32
+ depHooks.resolve({ name }, (koaBody) => patcher.patch(koaBody, {
33
+ name,
34
34
  patchType,
35
35
  post(data) {
36
36
  data.result = patcher.patch(data.result, {
37
- name: packageName,
37
+ name,
38
38
  patchType,
39
39
  pre(data) {
40
+ const { funcKey } = data;
40
41
  const sourceContext = core.scopes.sources.getStore()?.assess;
41
42
  const [ctx, origNext] = data.args;
42
43
 
43
44
  if (!sourceContext) {
44
- logger.error({ name: packageName }, 'unable to handle source. Missing `sourceContext`');
45
+ logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
45
46
  return;
46
47
  }
47
48
 
48
49
  if (sourceContext.parsedBody) {
49
- logger.trace({ name: packageName }, 'values already tracked');
50
+ logger.trace({ funcKey }, 'values already tracked');
50
51
  return;
51
52
  }
52
53
 
@@ -61,7 +62,7 @@ module.exports = (core) => {
61
62
  try {
62
63
  sources.handle({
63
64
  context: 'ctx.request.body',
64
- name: packageName,
65
+ name,
65
66
  inputType,
66
67
  stacktraceOpts: {
67
68
  constructorOpt: contrastNext,
@@ -72,7 +73,7 @@ module.exports = (core) => {
72
73
 
73
74
  sourceContext.parsedBody = !!Object.keys(ctx.request.body).length;
74
75
  } catch (err) {
75
- logger.error({ err, inputType, name: packageName }, 'unable to handle Koa source');
76
+ logger.error({ err, inputType, funcKey }, 'unable to handle Koa source');
76
77
  }
77
78
 
78
79
  await origNext(origErr);
@@ -0,0 +1,102 @@
1
+ /*
2
+ * Copyright: 2024 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
+ const { patchType } = require('../../common');
18
+ const { InputType } = require('@contrast/common');
19
+
20
+ module.exports = (core) => {
21
+ const {
22
+ depHooks,
23
+ logger,
24
+ patcher,
25
+ scopes,
26
+ assess: { dataflow: { sources } },
27
+ } = core;
28
+
29
+ function handler(req, constructorOpt) {
30
+ const sourceContext = scopes.sources.getStore()?.assess;
31
+ if (!sourceContext) return;
32
+
33
+ function handle(context, data, key) {
34
+ try {
35
+ sources.handle({
36
+ context,
37
+ data,
38
+ keys: [key],
39
+ name: 'multer',
40
+ inputType: InputType.BODY,
41
+ sourceContext,
42
+ stacktraceOpts: {
43
+ constructorOpt,
44
+ },
45
+ });
46
+ } catch (err) {
47
+ logger.error({ err }, 'error handling Koa multer Assess dataflow %s.%s source', context, key);
48
+ }
49
+ }
50
+
51
+ if (req.file) {
52
+ handle('req', req, 'file');
53
+ }
54
+
55
+ if (Array.isArray(req.files)) {
56
+ for (let i = 0; i < req.files.length; i++) {
57
+ handle('req.files', req.files[i], i);
58
+ }
59
+ }
60
+
61
+ if (req.body && Object.keys(req.body).length) {
62
+ handle('req', req, 'body');
63
+ }
64
+ }
65
+
66
+ function install() {
67
+ ['koa-multer', '@koa/multer'].forEach((name) => {
68
+ depHooks.resolve(
69
+ { name }, (_export) => {
70
+ const origMulter = _export;
71
+ return patcher.patch(_export, {
72
+ name,
73
+ patchType,
74
+ post(data) {
75
+ const { args, hooked } = data;
76
+ const instance = origMulter.apply(this, args);
77
+ const origMake = instance._makeMiddleware;
78
+ instance._makeMiddleware = function _makeMiddleware(...args) {
79
+ const origMulterMiddleware = origMake.apply(this, args);
80
+ return function multerMiddleware(req, res, origNext) {
81
+
82
+ const next = function(...args) {
83
+ handler(req, hooked);
84
+ return origNext.apply(this, args);
85
+ };
86
+ return origMulterMiddleware.apply(this, [req, res, next]);
87
+ };
88
+ };
89
+ data.result = instance;
90
+ }
91
+ });
92
+ }
93
+ );
94
+ });
95
+ }
96
+
97
+ const koaMulterInstrumentation = sources.koaInstrumentation.koaMulter = {
98
+ install
99
+ };
100
+
101
+ return koaMulterInstrumentation;
102
+ };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -32,22 +32,20 @@ module.exports = (core) => {
32
32
  depHooks.resolve(
33
33
  { name: router, file: 'lib/layer.js' },
34
34
  (layer) => {
35
- const name = `[${router}].layer.prototype`;
36
-
37
35
  layer.prototype = patcher.patch(layer.prototype, 'params', {
38
- name,
36
+ name: `[${router}].layer.prototype`,
39
37
  patchType,
40
- post({ orig, hooked, result }) {
38
+ post({ orig, hooked, result, name, funcKey }) {
41
39
  const sourceContext = core.scopes.sources.getStore()?.assess;
42
40
  const inputType = InputType.URL_PARAMETER;
43
41
 
44
42
  if (!sourceContext) {
45
- logger.error({ name, inputType }, 'unable to handle source. Missing `sourceContext`');
43
+ logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
46
44
  return;
47
45
  }
48
46
 
49
47
  if (sourceContext.parsedParams) {
50
- logger.trace({ name, inputType }, 'values already tracked');
48
+ logger.trace({ inputType, funcKey }, 'values already tracked');
51
49
  return;
52
50
  }
53
51
 
@@ -66,7 +64,7 @@ module.exports = (core) => {
66
64
 
67
65
  sourceContext.parsedParams = Boolean(Object.keys(result).length);
68
66
  } catch (err) {
69
- logger.error({ err, inputType, name }, 'unable to handle Koa source');
67
+ logger.error({ err, inputType, funcKey }, 'unable to handle Koa source');
70
68
  }
71
69
  }
72
70
  });
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -18,6 +18,8 @@
18
18
  const { InputType } = require('@contrast/common');
19
19
  const { patchType } = require('../../common');
20
20
 
21
+ const inputType = InputType.QUERYSTRING;
22
+
21
23
  /**
22
24
  * Function that exports an install method to patch Koa framework with our instrumentation
23
25
  * @param {Object} core - the core Contrast object in v5
@@ -36,58 +38,60 @@ module.exports = (core) => {
36
38
  */
37
39
  function install() {
38
40
  depHooks.resolve({ name: 'koa', version: '>=2.3.0' }, (Koa) => {
39
- function contrastStartMiddleware(ctx, next) {
40
- const name = 'Koa.Application';
41
- const sourceContext = core.scopes.sources.getStore()?.assess;
42
- const inputType = InputType.QUERYSTRING;
41
+ const createMiddleware = ({ name, funcKey }) => {
42
+ const contrastStartMiddleware = function contrastStartMiddleware(ctx, next) {
43
+ const sourceContext = core.scopes.sources.getStore()?.assess;
43
44
 
44
- if (!sourceContext) {
45
- logger.error({ name, inputType }, 'unable to handle Koa source. Missing `sourceContext`');
46
- return next();
47
- }
48
-
49
- // We check the contents mainly to trigger the getter for `ctx.query`
50
- // that is eventually set up by `koa-qs`
51
- if (ctx.query) {
52
- if (sourceContext.parsedQuery) {
53
- logger.trace({ name, inputType }, 'values already tracked');
45
+ if (!sourceContext) {
46
+ logger.error({ inputType, funcKey }, 'unable to handle Koa source. Missing `sourceContext`');
54
47
  return next();
55
48
  }
56
49
 
57
- try {
58
- sources.handle({
59
- context: 'ctx.query',
60
- data: ctx.query,
61
- inputType,
62
- name,
63
- stacktraceOpts: {
64
- constructorOpt: contrastStartMiddleware,
65
- },
66
- sourceContext
67
- });
68
-
69
- sourceContext.parsedQuery = true;
70
- } catch (err) {
71
- logger.error({ err, inputType, name }, 'unable to handle Koa source');
50
+ // We check the contents mainly to trigger the getter for `ctx.query`
51
+ // that is eventually set up by `koa-qs`
52
+ if (ctx.query) {
53
+ if (sourceContext.parsedQuery) {
54
+ logger.trace({ inputType, funcKey }, 'values already tracked');
55
+ return next();
56
+ }
57
+
58
+ try {
59
+ sources.handle({
60
+ context: 'ctx.query',
61
+ data: ctx.query,
62
+ inputType,
63
+ name,
64
+ stacktraceOpts: {
65
+ constructorOpt: contrastStartMiddleware,
66
+ },
67
+ sourceContext
68
+ });
69
+
70
+ sourceContext.parsedQuery = true;
71
+ } catch (err) {
72
+ logger.error({ err, inputType, funcKey }, 'unable to handle Koa source');
73
+ }
72
74
  }
73
- }
74
75
 
75
- return next();
76
- }
76
+ return next();
77
+ };
78
+
79
+ // mark these middleware as ours
80
+ contrastStartMiddleware._isContrastStartMiddleware = true;
77
81
 
78
- // mark these middleware as ours
79
- contrastStartMiddleware._isContrastStartMiddleware = true;
82
+ return contrastStartMiddleware;
83
+ };
80
84
 
81
85
  patcher.patch(Koa.prototype, 'use', {
82
86
  name: 'Koa.Application',
83
87
  patchType,
84
- pre({ obj: app }) {
88
+ pre({ obj: app, name, funcKey }) {
85
89
  // if not already inserted, insert the initial middleware.
86
90
  if (
87
91
  app.middleware &&
88
- (!app.middleware[0] || !app.middleware[0]._isContrastStartMiddleware)
92
+ (!app.middleware[0] || !app.middleware[0]._isContrastStartMiddleware)
89
93
  ) {
90
- app.middleware.splice(0, 0, contrastStartMiddleware);
94
+ app.middleware.unshift(createMiddleware({ name, funcKey }));
91
95
  }
92
96
  }
93
97
  });
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -23,36 +23,20 @@ module.exports = (core) => {
23
23
  depHooks,
24
24
  patcher,
25
25
  logger,
26
- assess,
27
- scopes: { wrap },
26
+ assess: { dataflow: { sources } },
27
+ scopes,
28
28
  } = core;
29
29
 
30
- function handleFile(sourceContext, constructorOpt, req) {
31
- try {
32
- assess.dataflow.sources.handle({
33
- context: 'req',
34
- data: req,
35
- keys: ['file'],
36
- name: 'multer',
37
- inputType: InputType.BODY,
38
- sourceContext,
39
- stacktraceOpts: {
40
- constructorOpt,
41
- },
42
- });
43
- } catch (err) {
44
- logger.error({ err }, 'error handling multer Assess dataflow req.file source');
45
- }
46
- }
30
+ function handler(req, constructorOpt) {
31
+ const sourceContext = scopes.sources.getStore()?.assess;
32
+ if (!sourceContext) return;
47
33
 
48
- function handleFiles(sourceContext, constructorOpt, req) {
49
- let i;
50
- for (i = 0; i < req.files.length; i++) {
34
+ function handle(context, data, key) {
51
35
  try {
52
- assess.dataflow.sources.handle({
53
- context: 'req.files',
54
- data: req.files[i],
55
- keys: [i],
36
+ sources.handle({
37
+ context,
38
+ data,
39
+ keys: [key],
56
40
  name: 'multer',
57
41
  inputType: InputType.BODY,
58
42
  sourceContext,
@@ -61,26 +45,22 @@ module.exports = (core) => {
61
45
  },
62
46
  });
63
47
  } catch (err) {
64
- logger.error({ err }, 'error handling multer Assess dataflow req.files[%s] source', i);
48
+ logger.error({ err }, 'error handling multer Assess dataflow %s.%s source', context, key);
65
49
  }
66
50
  }
67
- }
68
51
 
69
- function handleBody(sourceContext, constructorOpt, req) {
70
- try {
71
- assess.dataflow.sources.handle({
72
- context: 'req',
73
- data: req,
74
- keys: ['body'],
75
- name: 'multer',
76
- inputType: InputType.BODY,
77
- sourceContext,
78
- stacktraceOpts: {
79
- constructorOpt,
80
- },
81
- });
82
- } catch (err) {
83
- logger.error({ err }, 'error handling multer Assess dataflow req.body source');
52
+ if (req.file) {
53
+ handle('req', req, 'file');
54
+ }
55
+
56
+ if (Array.isArray(req.files)) {
57
+ for (let i = 0; i < req.files.length; i++) {
58
+ handle('req.files', req.files[i], i);
59
+ }
60
+ }
61
+
62
+ if (req.body && Object.keys(req.body).length) {
63
+ handle('req', req, 'body');
84
64
  }
85
65
  }
86
66
 
@@ -97,14 +77,8 @@ module.exports = (core) => {
97
77
  patchType,
98
78
  pre(data) {
99
79
  const [req, , next, hooked] = data.args;
100
-
101
- const sourceContext = core.scopes.sources.getStore()?.assess;
102
- if (!sourceContext) return;
103
-
104
- data.args[2] = wrap(function (...args) {
105
- if (req.file) handleFile(sourceContext, hooked, req);
106
- if (Array.isArray(req.files)) handleFiles(sourceContext, hooked, req);
107
- if (req.body && Object.keys(req.body).length) handleBody(sourceContext, hooked, req.body);
80
+ data.args[2] = scopes.wrap(function (...args) {
81
+ handler(req, hooked);
108
82
  next(...args);
109
83
  });
110
84
  },
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2023 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -18,6 +18,8 @@
18
18
  const { InputType } = require('@contrast/common');
19
19
  const { patchType } = require('../common');
20
20
 
21
+ const inputType = InputType.QUERYSTRING;
22
+
21
23
  module.exports = (core) => {
22
24
  const {
23
25
  depHooks,
@@ -33,17 +35,16 @@ module.exports = (core) => {
33
35
  (qs) => patcher.patch(qs, 'parse', {
34
36
  name,
35
37
  patchType,
36
- post({ args, hooked, orig, result }) {
38
+ post({ args, hooked, orig, result, funcKey }) {
37
39
  const sourceContext = core.scopes.sources.getStore()?.assess;
38
- const inputType = InputType.QUERYSTRING;
39
40
 
40
41
  if (!sourceContext) {
41
- logger.error({ inputType, name }, 'unable to handle source. Missing `sourceContext`');
42
+ logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
42
43
  return;
43
44
  }
44
45
 
45
46
  if (sourceContext.parsedQuery) {
46
- logger.trace({ inputType, name }, 'values already tracked');
47
+ logger.trace({ inputType, funcKey }, 'values already tracked');
47
48
  return;
48
49
  }
49
50
 
@@ -67,7 +68,7 @@ module.exports = (core) => {
67
68
 
68
69
  sourceContext.parsedQuery = true;
69
70
  } catch (err) {
70
- logger.error({ err, inputType, name }, 'unable to handle source');
71
+ logger.error({ err, inputType, funcKey }, 'unable to handle source');
71
72
  }
72
73
  }
73
74
  }