@contrast/protect 1.2.2 → 1.3.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 (47) hide show
  1. package/lib/cli-rewriter.js +15 -0
  2. package/lib/error-handlers/constants.js +15 -0
  3. package/lib/error-handlers/index.js +17 -0
  4. package/lib/error-handlers/install/express4.js +90 -0
  5. package/lib/error-handlers/install/fastify3.js +15 -0
  6. package/lib/error-handlers/install/koa2.js +15 -0
  7. package/lib/esm-loader.mjs +15 -0
  8. package/lib/index.d.ts +145 -19
  9. package/lib/index.js +15 -0
  10. package/lib/input-analysis/constants.js +20 -0
  11. package/lib/input-analysis/handlers.js +33 -10
  12. package/lib/input-analysis/index.js +36 -3
  13. package/lib/input-analysis/install/body-parser1.js +130 -0
  14. package/lib/input-analysis/install/cookie-parser1.js +82 -0
  15. package/lib/input-analysis/install/express4.js +108 -0
  16. package/lib/input-analysis/install/fastify3.js +47 -19
  17. package/lib/input-analysis/install/formidable1.js +73 -0
  18. package/lib/input-analysis/install/http.js +18 -1
  19. package/lib/input-analysis/install/koa-body5.js +68 -0
  20. package/lib/input-analysis/install/koa-bodyparser4.js +68 -0
  21. package/lib/input-analysis/install/koa2.js +26 -25
  22. package/lib/input-analysis/install/multer1.js +89 -0
  23. package/lib/input-analysis/install/qs6.js +61 -0
  24. package/lib/input-analysis/install/universal-cookie4.js +56 -0
  25. package/lib/input-tracing/constants.js +15 -0
  26. package/lib/input-tracing/handlers/index.js +154 -59
  27. package/lib/input-tracing/index.js +17 -0
  28. package/lib/input-tracing/install/child-process.js +16 -1
  29. package/lib/input-tracing/install/fs.js +17 -2
  30. package/lib/input-tracing/install/mongodb.js +233 -0
  31. package/lib/input-tracing/install/mysql.js +18 -2
  32. package/lib/input-tracing/install/postgres.js +15 -0
  33. package/lib/input-tracing/install/sequelize.js +15 -0
  34. package/lib/input-tracing/install/sqlite3.js +15 -0
  35. package/lib/make-response-blocker.js +15 -0
  36. package/lib/make-source-context.js +18 -0
  37. package/lib/security-exception.js +15 -0
  38. package/lib/throw-security-exception.js +17 -6
  39. package/lib/utils.js +14 -18
  40. package/package.json +8 -8
  41. package/lib/input-analysis/install/co-body.js +0 -51
  42. package/lib/input-analysis/install/cookie-parser.js +0 -48
  43. package/lib/input-analysis/install/formidable.js +0 -53
  44. package/lib/input-analysis/install/multer.js +0 -52
  45. package/lib/input-analysis/install/qs.js +0 -40
  46. package/lib/input-analysis/install/universal-cookie.js +0 -34
  47. package/lib/input-tracing/handlers/nosql-injection-mongo.js +0 -48
@@ -0,0 +1,130 @@
1
+ /*
2
+ * Copyright: 2022 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 { isSecurityException } = require('../../security-exception');
19
+
20
+ module.exports = (core) => {
21
+ const {
22
+ depHooks,
23
+ logger,
24
+ scopes: { sources },
25
+ protect: { inputAnalysis },
26
+ } = core;
27
+
28
+ function contrastNext(req, origNext, fnName) {
29
+ return function next(origErr) {
30
+ const sourceContext = sources.getStore()?.protect;
31
+ let securityException;
32
+
33
+ if (!sourceContext) {
34
+ logger.debug(`source context not available in \`body-parser\`'s \`${fnName}\` hook`);
35
+ } else {
36
+ if (req.body && Object.keys(req.body).length) {
37
+ sourceContext.parsedBody = req.body;
38
+
39
+ try {
40
+ inputAnalysis.handleParsedBody(sourceContext, req.body);
41
+ } catch (err) {
42
+ if (isSecurityException(err)) {
43
+ securityException = err;
44
+ } else {
45
+ logger.error({ err }, 'Unexpected error during input analysis');
46
+ }
47
+ }
48
+ }
49
+ }
50
+ const error = securityException || origErr;
51
+
52
+ origNext(error);
53
+ };
54
+ }
55
+
56
+ // Patch body parser - `body-parser` used by `express` framework
57
+ function install() {
58
+ depHooks.resolve({ name: 'body-parser' }, (bodyParser) => {
59
+ const origBodyParser = bodyParser;
60
+
61
+ const { json: origJson, raw: origRaw, text: origText, urlencoded: origUrlencoded } = bodyParser;
62
+ const fnArr = [
63
+ {
64
+ key: 'json',
65
+ original: origJson,
66
+ },
67
+ {
68
+ key: 'raw',
69
+ original: origRaw,
70
+ },
71
+ {
72
+ key: 'text',
73
+ original: origText,
74
+ },
75
+ {
76
+ key: 'urlencoded',
77
+ original: origUrlencoded,
78
+ }
79
+ ];
80
+
81
+ bodyParser = function bodyParser(...args) {
82
+ const parser = origBodyParser(...args);
83
+ const hookedParser = function(req, res, next) {
84
+ parser(req, res, contrastNext(req, next, 'bodyParser'));
85
+ };
86
+
87
+ Object.defineProperty(hookedParser, 'name', {
88
+ value: 'bodyParser'
89
+ });
90
+
91
+ return hookedParser;
92
+ };
93
+
94
+ fnArr.forEach((fn) => {
95
+ const fnName = `bodyParser.${fn.key}`;
96
+ function contrastHooked(...args) {
97
+ const parser = fn.original(...args);
98
+ const hookedParser = function (req, res, next) {
99
+ if (!req.body) {
100
+ parser(req, res, next);
101
+ } else {
102
+ parser(req, res, contrastNext(req, next, fnName));
103
+ }
104
+ };
105
+
106
+ Object.defineProperty(hookedParser, 'name', {
107
+ value: `${fn.key}Parser`
108
+ });
109
+
110
+ return hookedParser;
111
+ }
112
+
113
+ Object.defineProperty(bodyParser, fn.key, {
114
+ configurable: true,
115
+ enumerable: true,
116
+ get: () => contrastHooked,
117
+ });
118
+ });
119
+
120
+ return bodyParser;
121
+ }
122
+ );
123
+ }
124
+
125
+ const bodyParser1Instrumentation = inputAnalysis.bodyParser1Instrumentation = {
126
+ install
127
+ };
128
+
129
+ return bodyParser1Instrumentation;
130
+ };
@@ -0,0 +1,82 @@
1
+ /*
2
+ * Copyright: 2022 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('../constants');
19
+ const { isSecurityException } = require('../../security-exception');
20
+
21
+ module.exports = (core) => {
22
+ const {
23
+ depHooks,
24
+ patcher,
25
+ logger,
26
+ scopes: { sources },
27
+ protect: { inputAnalysis },
28
+ } = core;
29
+
30
+ // Patch `cookie-parser` package
31
+ function install() {
32
+ depHooks.resolve({ name: 'cookie-parser' }, (cookieParser) => patcher.patch(cookieParser, {
33
+ name: 'cookie-parser',
34
+ patchType,
35
+ post(data) {
36
+ data.result = patcher.patch(data.result, {
37
+ name: 'cookie-parser',
38
+ patchType,
39
+ pre(data) {
40
+ const [req, , origNext] = data.args;
41
+
42
+ function contrastNext(origErr) {
43
+ const sourceContext = sources.getStore()?.protect;
44
+ let securityException;
45
+
46
+ if (!sourceContext) {
47
+ logger.debug('source context not available in `cookie-parser` hook');
48
+ } else {
49
+ if ((req.cookies && Object.keys(req.cookies).length) || (req.signedCookies && Object.keys(req.signedCookies).length)) {
50
+ sourceContext.parsedCookies = { ...req.cookies, ...req.signedCookies };
51
+
52
+ try {
53
+ inputAnalysis.handleCookies(sourceContext, sourceContext.parsedCookies);
54
+ } catch (err) {
55
+ if (isSecurityException(err)) {
56
+ securityException = err;
57
+ } else {
58
+ logger.error({ err }, 'Unexpected error during input analysis');
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ const error = securityException || origErr;
65
+
66
+ origNext(error);
67
+ }
68
+
69
+ data.args[2] = contrastNext;
70
+ }
71
+ });
72
+ }
73
+ })
74
+ );
75
+ }
76
+
77
+ const cookieParser1Instrumentation = inputAnalysis.cookieParser1Instrumentation = {
78
+ install
79
+ };
80
+
81
+ return cookieParser1Instrumentation;
82
+ };
@@ -0,0 +1,108 @@
1
+ /*
2
+ * Copyright: 2022 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('../constants');
19
+ const { isSecurityException } = require('../../security-exception');
20
+
21
+ /**
22
+ * Function that exports an install method to patch Express framework with our instrumentation
23
+ * @param {Object} core - the core Contrast object in v5
24
+ * @return {Object} object with install method and the other relative functions exported for testing purposes
25
+ */
26
+ module.exports = (core) => {
27
+ const {
28
+ depHooks,
29
+ patcher,
30
+ logger,
31
+ scopes: { sources },
32
+ protect: { inputAnalysis },
33
+ } = core;
34
+
35
+ /**
36
+ * registers a depHook for express module instrumentation
37
+ */
38
+ function install() {
39
+ depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/middleware/query.js' }, (query) => patcher.patch(query, {
40
+ name: 'Express.query',
41
+ patchType,
42
+ post(data) {
43
+ data.result = patcher.patch(data.result, {
44
+ name: 'Express.query',
45
+ patchType,
46
+ pre(data) {
47
+ const [req, , origNext] = data.args;
48
+
49
+ function contrastNext(origErr) {
50
+ const sourceContext = sources.getStore()?.protect;
51
+ let securityException;
52
+
53
+ if (!sourceContext) {
54
+ logger.debug('source context not available in `Express.query` hook');
55
+
56
+ // It is possible for the query to be already parsed by `qs`
57
+ // which means that we've already handled/analyzed it.
58
+ // So we check whether we already have the `parsedQuery` property in the context
59
+ } else if (req.query && Object.keys(req.query).length && (!('parsedQuery' in sourceContext))) {
60
+ sourceContext.parsedQuery = req.query;
61
+
62
+ try {
63
+ inputAnalysis.handleQueryParams(sourceContext, req.query);
64
+ } catch (err) {
65
+ if (isSecurityException(err)) {
66
+ securityException = err;
67
+ } else {
68
+ logger.error({ err }, 'Unexpected error during input analysis');
69
+ }
70
+ }
71
+ }
72
+
73
+ const error = securityException || origErr;
74
+
75
+ origNext(error);
76
+ }
77
+
78
+ data.args[2] = contrastNext;
79
+ }
80
+ });
81
+ }
82
+ }));
83
+
84
+ depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/layer.js' }, (Layer) => {
85
+ patcher.patch(Layer.prototype, 'handle_request', {
86
+ name: 'express.Layer.prototype.handle_request',
87
+ patchType,
88
+ pre(data) {
89
+ const { obj: { params } } = data;
90
+ const sourceContext = sources.getStore()?.protect;
91
+
92
+ if (!sourceContext) {
93
+ logger.debug('source context not available in `express.Layer.prototype.handle_request` hook');
94
+ } else if (params && Object.keys(params).length) {
95
+ sourceContext.parsedParams = params;
96
+ inputAnalysis.handleUrlParams(sourceContext, params);
97
+ }
98
+ }
99
+ });
100
+ });
101
+ }
102
+
103
+ const express4Instrumentation = inputAnalysis.express4Instrumentation = {
104
+ install
105
+ };
106
+
107
+ return express4Instrumentation;
108
+ };
@@ -1,5 +1,23 @@
1
+ /*
2
+ * Copyright: 2022 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
+
1
16
  'use strict';
2
17
 
18
+ const { patchType } = require('../constants');
19
+ const { isSecurityException } = require('../../security-exception');
20
+
3
21
  /**
4
22
  * Function that exports an install method to patch Fastify framework with our instrumentation
5
23
  * @param {Object} core - the core Contrast object in v5
@@ -29,7 +47,7 @@ module.exports = (core) => {
29
47
  function patchFastify(fastify) {
30
48
  return patcher.patch(fastify, {
31
49
  name: 'fastify.build',
32
- patchType: 'framework-patch',
50
+ patchType,
33
51
  post({ result: server }) {
34
52
  server.addHook('preValidation', preValidationHook);
35
53
  },
@@ -45,35 +63,45 @@ module.exports = (core) => {
45
63
  */
46
64
  function preValidationHook(request, reply, done) {
47
65
  const sourceContext = sources.getStore()?.protect;
66
+ let securityException;
48
67
 
49
68
  if (!sourceContext) {
50
69
  logger.debug('source context not available in fastify prevalidation hook');
51
70
  } else {
52
- if (request.params) {
53
- sourceContext.parsedParams = request.params;
54
- inputAnalysis.handleUrlParams(sourceContext, request.params);
55
- }
56
- if (request.cookies) {
57
- sourceContext.parsedCookies = request.cookies;
58
- inputAnalysis.handleCookies(sourceContext, request.cookies);
59
- }
60
- if (request.body) {
61
- sourceContext.parsedBody = request.body;
62
- inputAnalysis.handleParsedBody(sourceContext, request.body);
63
- }
71
+ try {
72
+ if (request.params) {
73
+ sourceContext.parsedParams = request.params;
74
+ inputAnalysis.handleUrlParams(sourceContext, request.params);
75
+ }
76
+ if (request.cookies) {
77
+ sourceContext.parsedCookies = request.cookies;
78
+ inputAnalysis.handleCookies(sourceContext, request.cookies);
79
+ }
80
+ if (request.body) {
81
+ sourceContext.parsedBody = request.body;
82
+ inputAnalysis.handleParsedBody(sourceContext, request.body);
83
+ }
64
84
 
65
- if (request.query) {
66
- sourceContext.parsedQuery = request.query;
67
- inputAnalysis.handleQueryParams(sourceContext, request.query);
85
+ if (request.query) {
86
+ sourceContext.parsedQuery = request.query;
87
+ inputAnalysis.handleQueryParams(sourceContext, request.query);
88
+ }
89
+ } catch (err) {
90
+ if (isSecurityException(err)) {
91
+ securityException = err;
92
+ } else {
93
+ logger.error({ err }, 'Unexpected error during input analysis');
94
+ }
68
95
  }
69
96
  }
70
- done();
97
+
98
+ done(securityException);
71
99
  }
72
100
 
73
- const fastifyInstrumentation = inputAnalysis.fastifyInstrumentation = {
101
+ const fastify3Instrumentation = inputAnalysis.fastify3Instrumentation = {
74
102
  preValidationHook,
75
103
  install,
76
104
  };
77
105
 
78
- return fastifyInstrumentation;
106
+ return fastify3Instrumentation;
79
107
  };
@@ -0,0 +1,73 @@
1
+ /*
2
+ * Copyright: 2022 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('../constants');
19
+
20
+ module.exports = (core) => {
21
+ const {
22
+ depHooks,
23
+ patcher,
24
+ logger,
25
+ scopes: { sources },
26
+ protect: { inputAnalysis },
27
+ } = core;
28
+
29
+ // Patch `formidable`
30
+ function install() {
31
+ depHooks.resolve({ name: 'formidable' }, (formidable) => {
32
+ formidable.IncomingForm.prototype.parse = patcher.patch(formidable.IncomingForm.prototype.parse, {
33
+ name: 'Formidable.IncomingForm.prototype.parse',
34
+ patchType,
35
+ pre(data) {
36
+ const origCb = data.args[1];
37
+
38
+ function hookedCb(...cbArgs) {
39
+ const sourceContext = sources.getStore()?.protect;
40
+ const [, fields, files] = cbArgs;
41
+
42
+ if (!sourceContext) {
43
+ logger.debug('source context not available in `formidable` hook');
44
+ } else {
45
+ if (fields) {
46
+ sourceContext.parsedBody = fields;
47
+ inputAnalysis.handleParsedBody(sourceContext, fields);
48
+ }
49
+ if (files) {
50
+ logger.debug('Check for vulnerable filename upload nyi');
51
+ // TODO: NODE-2601
52
+ }
53
+ }
54
+
55
+ if (origCb && typeof origCb === 'function') {
56
+ origCb.apply(this, cbArgs);
57
+ }
58
+ }
59
+
60
+ data.args[1] = hookedCb;
61
+ }
62
+ });
63
+
64
+ return formidable;
65
+ });
66
+ }
67
+
68
+ const formidable1Instrumentation = inputAnalysis.formidable1Instrumentation = {
69
+ install
70
+ };
71
+
72
+ return formidable1Instrumentation;
73
+ };
@@ -1,3 +1,18 @@
1
+ /*
2
+ * Copyright: 2022 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
+
1
16
  'use strict';
2
17
 
3
18
  const { Event } = require('@contrast/common');
@@ -39,7 +54,9 @@ class HttpInstrumentation {
39
54
  this.hookHttps();
40
55
  }
41
56
 
42
- uninstall() {}
57
+ uninstall() {
58
+ return null; //NYI
59
+ }
43
60
 
44
61
  /**
45
62
  * Sets hooks to instrument `http.Server.prototype`.
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Copyright: 2022 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('../constants');
19
+
20
+ module.exports = (core) => {
21
+ const {
22
+ depHooks,
23
+ patcher,
24
+ logger,
25
+ scopes: { sources },
26
+ protect: { inputAnalysis },
27
+ } = core;
28
+
29
+ // Patch `koa-body` package
30
+ function install() {
31
+ depHooks.resolve({ name: 'koa-body' }, (koaBody) => patcher.patch(koaBody, {
32
+ name: 'koa-body',
33
+ patchType,
34
+ post(data) {
35
+ data.result = patcher.patch(data.result, {
36
+ name: 'koa-body',
37
+ patchType,
38
+ pre(data) {
39
+ const [ctx, origNext] = data.args;
40
+
41
+ async function contrastNext(origErr) {
42
+ const sourceContext = sources.getStore()?.protect;
43
+
44
+ if (!sourceContext) {
45
+ logger.debug('source context not available in `koa-body` hook');
46
+ } else {
47
+ if (ctx.request.body && Object.keys(ctx.request.body).length) {
48
+ sourceContext.parsedBody = ctx.request.body;
49
+ inputAnalysis.handleParsedBody(sourceContext, ctx.request.body);
50
+ }
51
+ }
52
+
53
+ await origNext(origErr);
54
+ }
55
+
56
+ data.args[1] = contrastNext;
57
+ }
58
+ });
59
+ }
60
+ }));
61
+ }
62
+
63
+ const koaBody5Instrumentation = inputAnalysis.koaBody5Instrumentation = {
64
+ install
65
+ };
66
+
67
+ return koaBody5Instrumentation;
68
+ };
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Copyright: 2022 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('../constants');
19
+
20
+ module.exports = (core) => {
21
+ const {
22
+ depHooks,
23
+ patcher,
24
+ logger,
25
+ scopes: { sources },
26
+ protect: { inputAnalysis },
27
+ } = core;
28
+
29
+ // Patch `koa-bodyparser` package
30
+ function install() {
31
+ depHooks.resolve({ name: 'koa-bodyparser' }, (koaBodyparser) => patcher.patch(koaBodyparser, {
32
+ name: 'koa-bodyparser',
33
+ patchType,
34
+ post(data) {
35
+ data.result = patcher.patch(data.result, {
36
+ name: 'koa-bodyparser',
37
+ patchType,
38
+ pre(data) {
39
+ const [ctx, origNext] = data.args;
40
+
41
+ async function contrastNext(origErr) {
42
+ const sourceContext = sources.getStore()?.protect;
43
+
44
+ if (!sourceContext) {
45
+ logger.debug('source context not available in `koa-bodyparser` hook');
46
+ } else {
47
+ if (ctx.request.body && Object.keys(ctx.request.body).length) {
48
+ sourceContext.parsedBody = ctx.request.body;
49
+ inputAnalysis.handleParsedBody(sourceContext, ctx.request.body);
50
+ }
51
+ }
52
+
53
+ await origNext(origErr);
54
+ }
55
+
56
+ data.args[1] = contrastNext;
57
+ }
58
+ });
59
+ }
60
+ }));
61
+ }
62
+
63
+ const koaBodyparser4Instrumentation = inputAnalysis.koaBodyparser4Instrumentation = {
64
+ install
65
+ };
66
+
67
+ return koaBodyparser4Instrumentation;
68
+ };