@contrast/assess 1.3.0 → 1.5.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 (54) hide show
  1. package/lib/dataflow/event-factory.js +31 -78
  2. package/lib/dataflow/index.js +0 -1
  3. package/lib/dataflow/propagation/install/array-prototype-join.js +3 -3
  4. package/lib/dataflow/propagation/install/contrast-methods/add.js +23 -16
  5. package/lib/dataflow/propagation/install/contrast-methods/tag.js +30 -22
  6. package/lib/dataflow/propagation/install/decode-uri-component.js +3 -3
  7. package/lib/dataflow/propagation/install/ejs/escape-xml.js +3 -3
  8. package/lib/dataflow/propagation/install/encode-uri-component.js +3 -3
  9. package/lib/dataflow/propagation/install/escape-html.js +3 -3
  10. package/lib/dataflow/propagation/install/escape.js +3 -3
  11. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +3 -3
  12. package/lib/dataflow/propagation/install/mysql-connection-escape.js +3 -3
  13. package/lib/dataflow/propagation/install/pug-runtime-escape.js +3 -3
  14. package/lib/dataflow/propagation/install/querystring/parse.js +3 -3
  15. package/lib/dataflow/propagation/install/sql-template-strings.js +3 -3
  16. package/lib/dataflow/propagation/install/string/concat.js +4 -4
  17. package/lib/dataflow/propagation/install/string/format-methods.js +2 -2
  18. package/lib/dataflow/propagation/install/string/html-methods.js +5 -5
  19. package/lib/dataflow/propagation/install/string/index.js +5 -3
  20. package/lib/dataflow/propagation/install/string/match.js +122 -0
  21. package/lib/dataflow/propagation/install/string/replace.js +4 -4
  22. package/lib/dataflow/propagation/install/string/slice.js +104 -0
  23. package/lib/dataflow/propagation/install/string/split.js +4 -4
  24. package/lib/dataflow/propagation/install/string/substring.js +6 -4
  25. package/lib/dataflow/propagation/install/string/trim.js +2 -2
  26. package/lib/dataflow/propagation/install/unescape.js +3 -3
  27. package/lib/dataflow/propagation/install/url/domain-parsers.js +3 -3
  28. package/lib/dataflow/propagation/install/validator/hooks.js +2 -2
  29. package/lib/dataflow/sinks/index.js +11 -2
  30. package/lib/dataflow/sinks/install/child-process.js +87 -0
  31. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +29 -16
  32. package/lib/dataflow/sinks/install/http.js +21 -19
  33. package/lib/dataflow/sinks/install/koa/index.js +30 -0
  34. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +113 -0
  35. package/lib/dataflow/sinks/install/mssql.js +8 -6
  36. package/lib/dataflow/sinks/install/postgres.js +27 -17
  37. package/lib/dataflow/sinks/install/sqlite3.js +94 -0
  38. package/lib/dataflow/sources/handler.js +9 -6
  39. package/lib/dataflow/sources/index.js +9 -0
  40. package/lib/dataflow/sources/install/busboy1.js +112 -0
  41. package/lib/dataflow/sources/install/fastify/fastify.js +23 -29
  42. package/lib/dataflow/sources/install/fastify/index.js +4 -5
  43. package/lib/dataflow/sources/install/formidable1.js +91 -0
  44. package/lib/dataflow/sources/install/http.js +40 -47
  45. package/lib/dataflow/sources/install/koa/index.js +32 -0
  46. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +92 -0
  47. package/lib/dataflow/sources/install/koa/koa-routers.js +84 -0
  48. package/lib/dataflow/sources/install/koa/koa2.js +103 -0
  49. package/lib/dataflow/sources/install/qs6.js +84 -0
  50. package/lib/dataflow/utils/is-vulnerable.js +1 -1
  51. package/package.json +2 -2
  52. package/lib/dataflow/signatures/index.js +0 -1996
  53. package/lib/dataflow/signatures/mssql.js +0 -49
  54. package/lib/dataflow/sources/install/fastify/cookie.js +0 -61
@@ -0,0 +1,91 @@
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 { InputType } = require('@contrast/common');
19
+ const { patchType } = require('../common');
20
+
21
+ module.exports = (core) => {
22
+ const {
23
+ depHooks,
24
+ patcher,
25
+ logger,
26
+ assess: { dataflow: { sources } },
27
+ } = core;
28
+ const name = 'Formidable.IncomingForm.prototype.parse';
29
+
30
+
31
+ // Patch `formidable`
32
+ function install() {
33
+ depHooks.resolve({ name: 'formidable' }, (formidable) => {
34
+ formidable.IncomingForm.prototype.parse = patcher.patch(formidable.IncomingForm.prototype.parse, {
35
+ name,
36
+ patchType,
37
+ pre(data) {
38
+ const sourceContext = core.scopes.sources.getStore()?.assess;
39
+ const inputType = InputType.MULTIPART_VALUE;
40
+
41
+ if (!sourceContext) {
42
+ logger.error({ inputType, name }, 'unable to handle source. Missing `sourceContext`');
43
+ return;
44
+ }
45
+
46
+ if (sourceContext.parsedBody) {
47
+ logger.trace({ inputType, name }, 'values already tracked');
48
+ return;
49
+ }
50
+
51
+ const origCb = data.args[1];
52
+
53
+ data.args[1] = function hookedCb(...cbArgs) {
54
+ const [, fields] = cbArgs;
55
+
56
+ if (fields) {
57
+ try {
58
+ sources.handle({
59
+ context: 'req.body',
60
+ name,
61
+ inputType,
62
+ stacktraceOpts: {
63
+ constructorOpt: data.hooked,
64
+ prependFrames: [data.orig]
65
+ },
66
+ data: fields,
67
+ sourceContext
68
+ });
69
+ sourceContext.parsedBody = true;
70
+ } catch (err) {
71
+ logger.error({ err, inputType, name }, 'unable to handle source');
72
+ }
73
+ }
74
+
75
+ if (origCb && typeof origCb === 'function') {
76
+ origCb.apply(this, cbArgs);
77
+ }
78
+ };
79
+ }
80
+ });
81
+
82
+ return formidable;
83
+ });
84
+ }
85
+
86
+ const formidable1Instrumentation = sources.formidable1Instrumentation = {
87
+ install
88
+ };
89
+
90
+ return formidable1Instrumentation;
91
+ };
@@ -15,14 +15,13 @@
15
15
 
16
16
  'use strict';
17
17
  const { patchType } = require('../common');
18
- const { toLowerCase } = require('@contrast/common');
18
+ const { toLowerCase, InputType } = require('@contrast/common');
19
19
 
20
- // This is only just initiating an async storage for the http source
21
- // TODO Tracking the user input
22
20
  module.exports = function(core) {
23
21
  const {
24
22
  scopes: { sources },
25
23
  instrumentation: { instrument },
24
+ assess: { dataflow: { sources: dataflowSources } },
26
25
  patcher,
27
26
  } = core;
28
27
 
@@ -40,7 +39,7 @@ module.exports = function(core) {
40
39
  }
41
40
 
42
41
  try {
43
- const [, req, response] = data.args;
42
+ const [, req, res] = data.args;
44
43
  const store = sources.getStore();
45
44
 
46
45
  if (!store) {
@@ -48,7 +47,7 @@ module.exports = function(core) {
48
47
  return next();
49
48
  }
50
49
 
51
- patcher.patch(response, 'writeHead', {
50
+ patcher.patch(res, 'writeHead', {
52
51
  name: 'write-head',
53
52
  patchType,
54
53
  pre(data) {
@@ -74,7 +73,7 @@ module.exports = function(core) {
74
73
  }
75
74
  });
76
75
 
77
- patcher.patch(response, 'setHeader', {
76
+ patcher.patch(res, 'setHeader', {
78
77
  alwaysRun: true,
79
78
  name: 'set-header',
80
79
  patchType,
@@ -98,14 +97,42 @@ module.exports = function(core) {
98
97
  }
99
98
 
100
99
  const headers = {};
100
+ const sourceInputType = InputType.HEADER;
101
+ const sourceName = 'ClientRequest';
102
+
103
+ store.assess = {
104
+ responseData: {},
105
+ sourceEventsCount: 0,
106
+ propagationEventsCount: 0,
107
+ findings: {},
108
+ };
109
+
110
+ try {
111
+ dataflowSources.handle({
112
+ context: 'req.headers',
113
+ data: req.headers,
114
+ inputType: sourceInputType,
115
+ name: sourceName,
116
+ stacktraceOpts: {
117
+ constructorOpt: data.hooked,
118
+ prependFrames: [data.orig]
119
+ },
120
+ sourceContext: store.assess
121
+ });
122
+
123
+ } catch (err) {
124
+ logger.error({ err, inputType: sourceInputType, name: sourceName }, 'unable to handle http source');
125
+ }
101
126
 
102
127
  for (let i = 0; i < req.rawHeaders.length; i += 2) {
103
128
  const header = toLowerCase(req.rawHeaders[i]);
104
129
  headers[header] = req.rawHeaders[i + 1];
130
+ req.rawHeaders[i + 1] = req.headers[header];
105
131
  }
106
132
 
107
133
  const contentType = headers['content-type'] && toLowerCase(headers['content-type']);
108
- const reqData = {
134
+
135
+ store.assess.reqData = {
109
136
  ip: req.socket.remoteAddress,
110
137
  httpVersion: req.httpVersion,
111
138
  method: req.method,
@@ -115,13 +142,6 @@ module.exports = function(core) {
115
142
  contentType,
116
143
  };
117
144
 
118
- store.assess = {
119
- reqData,
120
- responseData: {},
121
- sourceEventsCount: 0,
122
- propagationEventsCount: 0,
123
- findings: {},
124
- };
125
145
  } catch (err) {
126
146
  logger.error({ err }, 'Error during assess request handling');
127
147
  }
@@ -132,47 +152,20 @@ module.exports = function(core) {
132
152
  }
133
153
 
134
154
  function install() {
135
- [{
136
- moduleName: 'http'
137
- },
138
- {
139
- moduleName: 'https'
140
- },
141
- {
142
- moduleName: 'spdy'
143
- },
144
- {
145
- moduleName: 'http2',
146
- patchObjectsProps: [
147
- {
148
- methods: ['createServer', 'createSecureServer'],
149
- patchType,
150
- patchObjects: [
151
- {
152
- name: 'Server.prototype',
153
- methods: ['emit'],
154
- patchType,
155
- around
156
- }
157
- ]
158
- }
159
- ]
160
- }].forEach(({ moduleName, patchObjectsProps }) => {
161
- const patchObjects = patchObjectsProps || [
162
- {
155
+ ['http', 'https', 'spdy', 'http2'].forEach((moduleName) => {
156
+ instrument({
157
+ moduleName,
158
+ patchObjects: [{
163
159
  name: 'Server.prototype',
164
160
  methods: ['emit'],
165
161
  patchType,
166
162
  around
167
- }
168
- ];
169
- instrument({
170
- moduleName,
171
- patchObjects
163
+ }]
172
164
  });
173
165
  });
174
166
  }
175
167
 
168
+ /* c8 ignore next 3 */
176
169
  function uninstall() {
177
170
  return null;
178
171
  }
@@ -0,0 +1,32 @@
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 { callChildComponentMethodsSync } = require('@contrast/common');
19
+
20
+ module.exports = function(core) {
21
+ const koaSources = core.assess.dataflow.sources.koaInstrumentation = {};
22
+
23
+ require('./koa2')(core);
24
+ require('./koa-bodyparsers')(core);
25
+ require('./koa-routers')(core);
26
+
27
+ koaSources.install = function install() {
28
+ callChildComponentMethodsSync(koaSources, 'install');
29
+ };
30
+
31
+ return koaSources;
32
+ };
@@ -0,0 +1,92 @@
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 { InputType } = require('@contrast/common');
19
+ const { patchType } = require('../../common');
20
+
21
+ module.exports = (core) => {
22
+ const {
23
+ depHooks,
24
+ patcher,
25
+ logger,
26
+ assess: { dataflow: { sources } },
27
+ } = core;
28
+
29
+ // Patch `koa-body` v4.x.x and `koa-bodyparser` v5.x.x packages
30
+ function install() {
31
+ ['koa-body', 'koa-bodyparser'].forEach(function (packageName) {
32
+ depHooks.resolve({ name: packageName }, (koaBody) => patcher.patch(koaBody, {
33
+ name: packageName,
34
+ patchType,
35
+ post(data) {
36
+ data.result = patcher.patch(data.result, {
37
+ name: packageName,
38
+ patchType,
39
+ pre(data) {
40
+ const sourceContext = core.scopes.sources.getStore()?.assess;
41
+ const [ctx, origNext] = data.args;
42
+
43
+ if (!sourceContext) {
44
+ logger.error({ name: packageName }, 'unable to handle source. Missing `sourceContext`');
45
+ return;
46
+ }
47
+
48
+ if (sourceContext.parsedBody) {
49
+ logger.trace({ name: packageName }, 'values already tracked');
50
+ return;
51
+ }
52
+
53
+
54
+ data.args[1] = async function contrastNext(origErr) {
55
+ const inputType = sourceContext.reqData.headers?.['content-type']?.includes('/json')
56
+ ? InputType.JSON_VALUE
57
+ : typeof ctx.request.body == 'object'
58
+ ? InputType.PARAMETER_VALUE
59
+ : InputType.BODY;
60
+
61
+ try {
62
+ sources.handle({
63
+ context: 'ctx.request.body',
64
+ name: packageName,
65
+ inputType,
66
+ stacktraceOpts: {
67
+ constructorOpt: contrastNext,
68
+ },
69
+ data: ctx.request.body,
70
+ sourceContext
71
+ });
72
+
73
+ sourceContext.parsedBody = !!Object.keys(ctx.request.body).length;
74
+ } catch (err) {
75
+ logger.error({ err, inputType, name: packageName }, 'unable to handle Koa source');
76
+ }
77
+
78
+ await origNext(origErr);
79
+ };
80
+ }
81
+ });
82
+ }
83
+ }));
84
+ });
85
+ }
86
+
87
+ const koaBodyparsersInstrumentation = sources.koaInstrumentation.koaBodyparsers = {
88
+ install
89
+ };
90
+
91
+ return koaBodyparsersInstrumentation;
92
+ };
@@ -0,0 +1,84 @@
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 { InputType } = require('@contrast/common');
19
+ const { patchType } = require('../../common');
20
+
21
+ module.exports = (core) => {
22
+ const {
23
+ depHooks,
24
+ patcher,
25
+ logger,
26
+ assess: { dataflow: { sources } },
27
+ } = core;
28
+
29
+ // Patch `koa-router` and `@koa/router` to handle parsed params
30
+ function install() {
31
+ ['koa-router', '@koa/router'].forEach(router => {
32
+ depHooks.resolve(
33
+ { name: router, file: 'lib/layer.js' },
34
+ (layer) => {
35
+ const name = `[${router}].layer.prototype`;
36
+
37
+ layer.prototype = patcher.patch(layer.prototype, 'params', {
38
+ name,
39
+ patchType,
40
+ post({ orig, hooked, result }) {
41
+ const sourceContext = core.scopes.sources.getStore()?.assess;
42
+ const inputType = InputType.URL_PARAMETER;
43
+
44
+ if (!sourceContext) {
45
+ logger.error({ name, inputType }, 'unable to handle source. Missing `sourceContext`');
46
+ return;
47
+ }
48
+
49
+ if (sourceContext.parsedParams) {
50
+ logger.trace({ name, inputType }, 'values already tracked');
51
+ return;
52
+ }
53
+
54
+ try {
55
+ sources.handle({
56
+ context: 'ctx.params',
57
+ data: result,
58
+ inputType,
59
+ name,
60
+ stacktraceOpts: {
61
+ constructorOpt: hooked,
62
+ prependFrames: [orig]
63
+ },
64
+ sourceContext
65
+ });
66
+
67
+ sourceContext.parsedParams = Boolean(Object.keys(result).length);
68
+ } catch (err) {
69
+ logger.error({ err, inputType, name }, 'unable to handle Koa source');
70
+ }
71
+ }
72
+ });
73
+
74
+ return layer;
75
+ });
76
+ });
77
+ }
78
+
79
+ const koaRoutersInstrumentation = sources.koaInstrumentation.koaRouters = {
80
+ install
81
+ };
82
+
83
+ return koaRoutersInstrumentation;
84
+ };
@@ -0,0 +1,103 @@
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 { InputType } = require('@contrast/common');
19
+ const { patchType } = require('../../common');
20
+
21
+ /**
22
+ * Function that exports an install method to patch Koa 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
+ logger,
29
+ depHooks,
30
+ patcher,
31
+ assess: { dataflow: { sources } },
32
+ } = core;
33
+
34
+ /**
35
+ * registers a depHook for koa module instrumentation
36
+ */
37
+ function install() {
38
+ 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;
43
+
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');
54
+ return next();
55
+ }
56
+
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');
72
+ }
73
+ }
74
+
75
+ return next();
76
+ }
77
+
78
+ // mark these middleware as ours
79
+ contrastStartMiddleware._isContrastStartMiddleware = true;
80
+
81
+ patcher.patch(Koa.prototype, 'use', {
82
+ name: 'Koa.Application',
83
+ patchType,
84
+ pre({ obj: app }) {
85
+ // if not already inserted, insert the initial middleware.
86
+ if (
87
+ app.middleware &&
88
+ (!app.middleware[0] || !app.middleware[0]._isContrastStartMiddleware)
89
+ ) {
90
+ app.middleware.splice(0, 0, contrastStartMiddleware);
91
+ }
92
+ }
93
+ });
94
+
95
+ });
96
+ }
97
+
98
+ const koa2Instrumentation = sources.koaInstrumentation.koa2 = {
99
+ install
100
+ };
101
+
102
+ return koa2Instrumentation;
103
+ };
@@ -0,0 +1,84 @@
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 { InputType } = require('@contrast/common');
19
+ const { patchType } = require('../common');
20
+
21
+ module.exports = (core) => {
22
+ const {
23
+ depHooks,
24
+ patcher,
25
+ logger,
26
+ assess: { dataflow: { sources } },
27
+ } = core;
28
+
29
+ // Patch `qs`
30
+ function install() {
31
+ const name = 'qs.parse';
32
+ depHooks.resolve({ name: 'qs' },
33
+ (qs) => patcher.patch(qs, 'parse', {
34
+ name,
35
+ patchType,
36
+ post({ args, hooked, orig, result }) {
37
+ const sourceContext = core.scopes.sources.getStore()?.assess;
38
+ const inputType = InputType.QUERYSTRING;
39
+
40
+ if (!sourceContext) {
41
+ logger.error({ inputType, name }, 'unable to handle source. Missing `sourceContext`');
42
+ return;
43
+ }
44
+
45
+ if (sourceContext.parsedQuery) {
46
+ logger.trace({ inputType, name }, 'values already tracked');
47
+ return;
48
+ }
49
+
50
+ // We need to run analysis for the `qs` result only when it's used as a query parser.
51
+ // `qs` is used also for parsing bodies, but these cases we handle individually with
52
+ // the respective library that's using it (e.g. `formidable`, `co-body`) because in
53
+ // some cases its use is optional and we cannot rely on it.
54
+ if (sourceContext.reqData?.queries === args[0]) {
55
+ try {
56
+ sources.handle({
57
+ context: 'req.query',
58
+ name,
59
+ inputType,
60
+ stacktraceOpts: {
61
+ constructorOpt: hooked,
62
+ prependFrames: [orig]
63
+ },
64
+ data: result,
65
+ sourceContext
66
+ });
67
+
68
+ sourceContext.parsedQuery = true;
69
+ } catch (err) {
70
+ logger.error({ err, inputType, name }, 'unable to handle source');
71
+ }
72
+ }
73
+ }
74
+ })
75
+ );
76
+ }
77
+
78
+ const qs6Instrumentation = sources.qs6Instrumentation = {
79
+ install
80
+ };
81
+
82
+ return qs6Instrumentation;
83
+ };
84
+
@@ -15,7 +15,7 @@
15
15
  'use strict';
16
16
 
17
17
  function mergeOverlapRange(overlapTags) {
18
- if (overlapTags.length <= 1) return overlapTags;
18
+ if (overlapTags.length <= 1 && overlapTags[0].length === 2) return overlapTags;
19
19
 
20
20
  const tagsInTuple = [];
21
21
  for (const overTagsCurr of overlapTags) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/assess",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -15,7 +15,7 @@
15
15
  "dependencies": {
16
16
  "@contrast/distringuish": "^4.1.0",
17
17
  "@contrast/scopes": "1.3.0",
18
- "@contrast/common": "1.6.0",
18
+ "@contrast/common": "1.8.0",
19
19
  "parseurl": "^1.3.3"
20
20
  }
21
21
  }