@contrast/assess 1.35.0 → 1.36.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 (168) hide show
  1. package/lib/crypto-analysis/install/crypto.js +1 -1
  2. package/lib/dataflow/propagation/install/JSON/parse-fn.js +1 -1
  3. package/lib/dataflow/propagation/install/JSON/parse.js +3 -2
  4. package/lib/dataflow/propagation/install/JSON/parse.test.js +2 -2
  5. package/lib/dataflow/propagation/install/JSON/stringify.js +11 -10
  6. package/lib/dataflow/propagation/install/JSON/stringify.test.js +3 -3
  7. package/lib/dataflow/propagation/install/array-prototype-join.js +4 -3
  8. package/lib/dataflow/propagation/install/array-prototype-join.test.js +3 -3
  9. package/lib/dataflow/propagation/install/buffer.js +2 -3
  10. package/lib/dataflow/propagation/install/contrast-methods/tag.test.js +2 -2
  11. package/lib/dataflow/propagation/install/decode-uri-component.js +5 -8
  12. package/lib/dataflow/propagation/install/decode-uri-component.test.js +1 -1
  13. package/lib/dataflow/propagation/install/ejs/escape-xml.js +6 -9
  14. package/lib/dataflow/propagation/install/ejs/escape-xml.test.js +2 -2
  15. package/lib/dataflow/propagation/install/ejs/template.js +2 -2
  16. package/lib/dataflow/propagation/install/encode-uri.js +4 -6
  17. package/lib/dataflow/propagation/install/encode-uri.test.js +2 -2
  18. package/lib/dataflow/propagation/install/escape-html.js +5 -8
  19. package/lib/dataflow/propagation/install/escape-html.test.js +3 -3
  20. package/lib/dataflow/propagation/install/escape.js +5 -8
  21. package/lib/dataflow/propagation/install/escape.test.js +2 -2
  22. package/lib/dataflow/propagation/install/fastify-send.js +3 -5
  23. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +6 -9
  24. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.test.js +1 -1
  25. package/lib/dataflow/propagation/install/joi/boolean.js +50 -52
  26. package/lib/dataflow/propagation/install/joi/expression.js +3 -10
  27. package/lib/dataflow/propagation/install/joi/index.js +98 -101
  28. package/lib/dataflow/propagation/install/joi/keys.js +10 -5
  29. package/lib/dataflow/propagation/install/joi/number.js +50 -52
  30. package/lib/dataflow/propagation/install/joi/string-schema.js +9 -14
  31. package/lib/dataflow/propagation/install/joi/utils.js +7 -4
  32. package/lib/dataflow/propagation/install/joi/values.js +5 -7
  33. package/lib/dataflow/propagation/install/mongoose/schema-map.js +5 -4
  34. package/lib/dataflow/propagation/install/mongoose/schema-map.test.js +4 -4
  35. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +5 -4
  36. package/lib/dataflow/propagation/install/mongoose/schema-mixed.test.js +4 -5
  37. package/lib/dataflow/propagation/install/mongoose/schema-string.js +3 -4
  38. package/lib/dataflow/propagation/install/mustache-escape.js +5 -8
  39. package/lib/dataflow/propagation/install/mustache-escape.test.js +2 -2
  40. package/lib/dataflow/propagation/install/mysql-connection-escape.js +5 -8
  41. package/lib/dataflow/propagation/install/mysql-connection-escape.test.js +2 -2
  42. package/lib/dataflow/propagation/install/parse-int.js +3 -3
  43. package/lib/dataflow/propagation/install/path/basename.js +7 -12
  44. package/lib/dataflow/propagation/install/path/basename.test.js +2 -2
  45. package/lib/dataflow/propagation/install/path/common.js +2 -2
  46. package/lib/dataflow/propagation/install/path/dirname.js +5 -10
  47. package/lib/dataflow/propagation/install/path/dirname.test.js +2 -2
  48. package/lib/dataflow/propagation/install/path/extname.js +6 -11
  49. package/lib/dataflow/propagation/install/path/extname.test.js +2 -2
  50. package/lib/dataflow/propagation/install/path/format.js +7 -13
  51. package/lib/dataflow/propagation/install/path/format.test.js +2 -2
  52. package/lib/dataflow/propagation/install/path/join-and-resolve.js +7 -12
  53. package/lib/dataflow/propagation/install/path/join-and-resolve.test.js +2 -2
  54. package/lib/dataflow/propagation/install/path/normalize.js +4 -11
  55. package/lib/dataflow/propagation/install/path/normalize.test.js +2 -2
  56. package/lib/dataflow/propagation/install/path/parse.js +3 -8
  57. package/lib/dataflow/propagation/install/path/parse.test.js +2 -2
  58. package/lib/dataflow/propagation/install/path/relative.js +5 -11
  59. package/lib/dataflow/propagation/install/path/relative.test.js +2 -2
  60. package/lib/dataflow/propagation/install/path/toNamespacedPath.js +5 -11
  61. package/lib/dataflow/propagation/install/path/toNamespacedPath.test.js +2 -2
  62. package/lib/dataflow/propagation/install/pug/index.js +8 -3
  63. package/lib/dataflow/propagation/install/pug-runtime-escape.js +5 -8
  64. package/lib/dataflow/propagation/install/pug-runtime-escape.test.js +1 -1
  65. package/lib/dataflow/propagation/install/querystring/escape.js +3 -3
  66. package/lib/dataflow/propagation/install/querystring/parse.js +7 -11
  67. package/lib/dataflow/propagation/install/querystring/stringify.js +3 -3
  68. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +4 -3
  69. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.test.js +5 -3
  70. package/lib/dataflow/propagation/install/send.js +5 -10
  71. package/lib/dataflow/propagation/install/sequelize/query-generator.js +3 -4
  72. package/lib/dataflow/propagation/install/sequelize/sql-string.js +8 -12
  73. package/lib/dataflow/propagation/install/sequelize/sql-string.test.js +2 -13
  74. package/lib/dataflow/propagation/install/sql-template-strings.js +3 -5
  75. package/lib/dataflow/propagation/install/sql-template-strings.test.js +2 -2
  76. package/lib/dataflow/propagation/install/string/concat.js +2 -1
  77. package/lib/dataflow/propagation/install/string/concat.test.js +15 -2
  78. package/lib/dataflow/propagation/install/string/format-methods.js +4 -2
  79. package/lib/dataflow/propagation/install/string/format-methods.test.js +15 -2
  80. package/lib/dataflow/propagation/install/string/html-methods.js +1 -1
  81. package/lib/dataflow/propagation/install/string/html-methods.test.js +15 -2
  82. package/lib/dataflow/propagation/install/string/index.js +2 -2
  83. package/lib/dataflow/propagation/install/string/match-all.js +2 -1
  84. package/lib/dataflow/propagation/install/string/match-all.test.js +13 -0
  85. package/lib/dataflow/propagation/install/string/match.js +11 -10
  86. package/lib/dataflow/propagation/install/string/match.test.js +13 -0
  87. package/lib/dataflow/propagation/install/string/replace.js +15 -9
  88. package/lib/dataflow/propagation/install/string/replace.test.js +13 -0
  89. package/lib/dataflow/propagation/install/string/slice.js +2 -1
  90. package/lib/dataflow/propagation/install/string/slice.test.js +13 -0
  91. package/lib/dataflow/propagation/install/string/split.js +2 -1
  92. package/lib/dataflow/propagation/install/string/split.test.js +13 -0
  93. package/lib/dataflow/propagation/install/string/substring.js +2 -1
  94. package/lib/dataflow/propagation/install/string/substring.test.js +13 -0
  95. package/lib/dataflow/propagation/install/string/trim.js +4 -1
  96. package/lib/dataflow/propagation/install/string/trim.test.js +13 -0
  97. package/lib/dataflow/propagation/install/unescape.js +5 -8
  98. package/lib/dataflow/propagation/install/unescape.test.js +2 -2
  99. package/lib/dataflow/propagation/install/url/domain-parsers.js +4 -5
  100. package/lib/dataflow/propagation/install/url/domain-parsers.test.js +2 -2
  101. package/lib/dataflow/propagation/install/url/parse.js +3 -2
  102. package/lib/dataflow/propagation/install/url/parse.test.js +2 -2
  103. package/lib/dataflow/propagation/install/url/searchParams.js +5 -5
  104. package/lib/dataflow/propagation/install/url/searchParams.test.js +2 -2
  105. package/lib/dataflow/propagation/install/url/url.js +6 -3
  106. package/lib/dataflow/propagation/install/url/url.test.js +2 -2
  107. package/lib/dataflow/propagation/install/util-format.js +7 -6
  108. package/lib/dataflow/propagation/install/util-format.test.js +2 -2
  109. package/lib/dataflow/propagation/install/validator/hooks.js +7 -2
  110. package/lib/dataflow/sinks/install/child-process.js +1 -1
  111. package/lib/dataflow/sinks/install/child-process.test.js +1 -1
  112. package/lib/dataflow/sinks/install/fs.js +1 -1
  113. package/lib/dataflow/sinks/install/fs.test.js +1 -1
  114. package/lib/dataflow/sinks/install/function.js +1 -1
  115. package/lib/dataflow/sinks/install/http/request.js +2 -1
  116. package/lib/dataflow/sinks/install/http/request.test.js +1 -1
  117. package/lib/dataflow/sinks/install/http/server-response.test.js +3 -5
  118. package/lib/dataflow/sinks/install/restify.js +1 -1
  119. package/lib/dataflow/sinks/install/vm.js +4 -2
  120. package/lib/dataflow/sinks/install/vm.test.js +1 -1
  121. package/lib/dataflow/sources/handler.js +5 -2
  122. package/lib/dataflow/sources/install/body-parser1.test.js +4 -4
  123. package/lib/dataflow/sources/install/busboy.js +8 -3
  124. package/lib/dataflow/sources/install/busboy.test.js +2 -2
  125. package/lib/dataflow/sources/install/cookie-parser1.test.js +2 -2
  126. package/lib/dataflow/sources/install/express/params.js +14 -11
  127. package/lib/dataflow/sources/install/express/params.test.js +5 -7
  128. package/lib/dataflow/sources/install/express/parsedUrl.js +3 -2
  129. package/lib/dataflow/sources/install/fastify/fastify.js +7 -6
  130. package/lib/dataflow/sources/install/fastify/fastify.test.js +2 -2
  131. package/lib/dataflow/sources/install/formidable1.js +7 -6
  132. package/lib/dataflow/sources/install/formidable1.test.js +2 -2
  133. package/lib/dataflow/sources/install/hapi/hapi.js +8 -10
  134. package/lib/dataflow/sources/install/hapi/hapi.test.js +0 -1
  135. package/lib/dataflow/sources/install/http.js +20 -16
  136. package/lib/dataflow/sources/install/http.test.js +28 -34
  137. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +7 -7
  138. package/lib/dataflow/sources/install/koa/koa-bodyparsers.test.js +3 -4
  139. package/lib/dataflow/sources/install/koa/koa-multer.js +8 -4
  140. package/lib/dataflow/sources/install/koa/koa-routers.js +7 -6
  141. package/lib/dataflow/sources/install/koa/koa-routers.test.js +2 -2
  142. package/lib/dataflow/sources/install/koa/koa2.js +7 -3
  143. package/lib/dataflow/sources/install/koa/koa2.test.js +1 -1
  144. package/lib/dataflow/sources/install/multer1.js +6 -2
  145. package/lib/dataflow/sources/install/qs6.js +1 -1
  146. package/lib/dataflow/sources/install/querystring.js +1 -1
  147. package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.js +1 -4
  148. package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.test.js +6 -8
  149. package/lib/dataflow/sources/install/restify/jsonBodyParser.js +0 -1
  150. package/lib/dataflow/sources/install/restify/jsonBodyParser.test.js +4 -8
  151. package/lib/dataflow/sources/install/restify/router.test.js +2 -2
  152. package/lib/dataflow/tag-utils.js +1 -1
  153. package/lib/dataflow/tracker.js +1 -1
  154. package/lib/dataflow/utils/is-safe-content-type.js +3 -2
  155. package/lib/event-factory.js +4 -4
  156. package/lib/get-policy.js +2 -2
  157. package/lib/index.js +18 -7
  158. package/lib/index.test.js +4 -0
  159. package/lib/make-source-context.js +37 -28
  160. package/lib/make-source-context.test.js +7 -7
  161. package/lib/response-scanning/handlers/index.js +7 -5
  162. package/lib/response-scanning/handlers/utils.js +11 -8
  163. package/lib/response-scanning/install/http.js +1 -1
  164. package/lib/sampler.js +136 -0
  165. package/lib/sampler.test.js +296 -0
  166. package/lib/session-configuration/install/express-session.js +1 -1
  167. package/lib/session-configuration/install/fastify-cookie.js +1 -1
  168. package/package.json +10 -10
@@ -16,10 +16,16 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
19
20
  const { patchType } = require('../../common');
20
21
 
21
22
  module.exports = function init(core) {
22
- const { depHooks, patcher, logger } = core;
23
+ const {
24
+ logger,
25
+ patcher,
26
+ depHooks,
27
+ assess: { getSourceContext },
28
+ } = core;
23
29
 
24
30
  core.assess.dataflow.sources.expressInstrumentation.params = {
25
31
  install() {
@@ -31,20 +37,17 @@ module.exports = function init(core) {
31
37
  name,
32
38
  patchType,
33
39
  post({ obj: layer, result, orig, hooked, funcKey }) {
34
-
35
40
  // we can exit early if
36
41
  // the layer doesn't match the request or
37
42
  // the layer doesn't recognize any parameters
38
- if (!result || !layer.keys || layer.keys.length === 0) {
39
- return;
40
- }
43
+ if (
44
+ !result ||
45
+ !layer.keys ||
46
+ layer.keys.length === 0
47
+ ) return;
41
48
 
42
- const sourceContext = core.scopes.sources.getStore()?.assess;
43
-
44
- if (!sourceContext) {
45
- logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
46
- return;
47
- }
49
+ const sourceContext = getSourceContext(SOURCE)
50
+ if (!sourceContext) return;
48
51
 
49
52
  if (sourceContext.parsedParams) {
50
53
  logger.trace({ funcKey }, 'values already tracked');
@@ -65,20 +65,18 @@ describe('assess dataflow sources express params', function () {
65
65
  });
66
66
  });
67
67
 
68
- it('does not call `.handle` when not in context', function () {
68
+ it('does not handle when there is no assess policy in request context', function () {
69
69
  simulateRequestScope(() => {
70
70
  Reflect.apply(Layer.prototype.match, layer, ['/bar']);
71
71
 
72
72
  expect(core.assess.dataflow.sources.handle).not.to.have.been.called;
73
- expect(core.logger.error).to.have.been.calledWith(
74
- { funcKey: 'assess-dataflow-source:Layer.prototype.match' },
75
- 'unable to handle source. Missing `sourceContext`'
76
- );
77
- }, {});
73
+ }, { assess: { policy: null } });
78
74
  });
79
75
 
80
76
  it('does not call `.handle` when the values are already tracked', function () {
81
77
  simulateRequestScope(() => {
78
+ core.scopes.sources.getStore().assess.parsedParams = true;
79
+
82
80
  Reflect.apply(Layer.prototype.match, layer, ['/bar']);
83
81
 
84
82
  expect(core.assess.dataflow.sources.handle).not.to.have.been.called;
@@ -86,7 +84,7 @@ describe('assess dataflow sources express params', function () {
86
84
  { funcKey: 'assess-dataflow-source:Layer.prototype.match' },
87
85
  'values already tracked'
88
86
  );
89
- }, { assess: { parsedParams: true } });
87
+ });
90
88
  });
91
89
 
92
90
  it('handles a case with an error in the `.handle` method', function () {
@@ -16,16 +16,17 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
19
20
  const { patchType } = require('../../common');
20
21
 
21
22
  module.exports = function init(core) {
22
23
  const {
23
24
  assess: {
25
+ getSourceContext,
24
26
  dataflow: { sources }
25
27
  },
26
28
  depHooks,
27
29
  patcher,
28
- scopes
29
30
  } = core;
30
31
 
31
32
  core.assess.dataflow.sources.expressInstrumentation.parsedUrl = {
@@ -48,7 +49,7 @@ module.exports = function init(core) {
48
49
  name: 'express.middleware.init.expressInit.next',
49
50
  patchType,
50
51
  pre(data) {
51
- const sourceContext = scopes.sources.getStore()?.assess;
52
+ const sourceContext = getSourceContext(SOURCE);
52
53
  if (!sourceContext) return;
53
54
 
54
55
  const sourceInfo = {
@@ -16,6 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
19
20
  const { patchType } = require('../../common');
20
21
 
21
22
  module.exports = function (core) {
@@ -23,7 +24,10 @@ module.exports = function (core) {
23
24
  logger,
24
25
  depHooks,
25
26
  patcher,
26
- assess: { dataflow: { sources } },
27
+ assess: {
28
+ getSourceContext,
29
+ dataflow: { sources }
30
+ },
27
31
  } = core;
28
32
 
29
33
  const source = sources.fastifyInstrumentation.fastify = {
@@ -38,12 +42,9 @@ module.exports = function (core) {
38
42
  : typeof request.body == 'object'
39
43
  ? InputType.PARAMETER_VALUE
40
44
  : InputType.BODY;
41
- const sourceContext = core.scopes.sources.getStore()?.assess;
45
+ const sourceContext = getSourceContext(SOURCE);
42
46
 
43
- if (!sourceContext) {
44
- logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
45
- return;
46
- }
47
+ if (!sourceContext) return;
47
48
 
48
49
  [
49
50
  { key: 'query', inputType: InputType.QUERYSTRING, alreadyTrackedFlag: 'parsedQuery' },
@@ -146,10 +146,10 @@ describe('assess dataflow sources fastify', function () {
146
146
  });
147
147
  });
148
148
 
149
- it('does not call `.handle` when not in context', function () {
149
+ it('does not handle source when there is no assess policy in request context', function () {
150
150
  simulateRequestScope(() => {
151
151
  fastifyServerMock();
152
- }, {});
152
+ }, { assess: { policy: null } });
153
153
 
154
154
  expect(sources.handle).not.to.have.been.called;
155
155
  });
@@ -16,6 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../constants');
19
20
  const { patchType } = require('../common');
20
21
 
21
22
  const inputType = InputType.MULTIPART_VALUE;
@@ -25,7 +26,10 @@ module.exports = (core) => {
25
26
  depHooks,
26
27
  patcher,
27
28
  logger,
28
- assess: { dataflow: { sources } },
29
+ assess: {
30
+ getSourceContext,
31
+ dataflow: { sources }
32
+ },
29
33
  } = core;
30
34
 
31
35
  const name = 'Formidable.IncomingForm.prototype.parse';
@@ -38,12 +42,9 @@ module.exports = (core) => {
38
42
  patchType,
39
43
  pre(data) {
40
44
  const { funcKey } = data;
41
- const sourceContext = core.scopes.sources.getStore()?.assess;
45
+ const sourceContext = getSourceContext(SOURCE);
42
46
 
43
- if (!sourceContext) {
44
- logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
45
- return;
46
- }
47
+ if (!sourceContext) return;
47
48
 
48
49
  if (sourceContext.parsedBody) {
49
50
  logger.trace({ inputType, funcKey }, 'values already tracked');
@@ -82,10 +82,10 @@ describe('assess dataflow sources formidable v1.x', function () {
82
82
  );
83
83
  });
84
84
 
85
- it('does not call `.handle` when not in context', function () {
85
+ it('does not call `.handle` when there is no assess policy in request context', function () {
86
86
  simulateRequestScope(() => {
87
87
  patchedParse(req, cbStub);
88
- }, {});
88
+ }, { assess: { policy: null } });
89
89
 
90
90
  expect(sources.handle).not.to.have.been.called;
91
91
  expect(cbStub).to.have.been.calledOnce;
@@ -16,6 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
19
20
  const { patchType } = require('../../common');
20
21
 
21
22
  module.exports = function (core) {
@@ -23,7 +24,10 @@ module.exports = function (core) {
23
24
  logger,
24
25
  depHooks,
25
26
  patcher,
26
- assess: { dataflow: { sources } },
27
+ assess: {
28
+ getSourceContext,
29
+ dataflow: { sources }
30
+ },
27
31
  } = core;
28
32
 
29
33
  const source = sources.hapiInstrumentation.hapi = {
@@ -36,11 +40,8 @@ module.exports = function (core) {
36
40
  post({ result: server, funcKey, hooked, orig }) {
37
41
 
38
42
  server.ext('onRequest', (req, h) => {
39
- const sourceContext = core.scopes.sources.getStore()?.assess;
40
- if (!sourceContext) {
41
- logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
42
- return;
43
- }
43
+ const sourceContext = getSourceContext(SOURCE);
44
+ if (!sourceContext) return;
44
45
 
45
46
  [
46
47
  { key: 'query', inputType: InputType.QUERYSTRING, trackedFlag: 'parsedQuery' },
@@ -72,10 +73,7 @@ module.exports = function (core) {
72
73
  });
73
74
  server.ext('onPostAuth', (req, h) => {
74
75
  const sourceContext = core.scopes.sources.getStore()?.assess;
75
- if (!sourceContext) {
76
- logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
77
- return;
78
- }
76
+ if (!sourceContext) return;
79
77
 
80
78
  [
81
79
  { key: 'state', inputType: InputType.COOKIE_VALUE, trackedFlag: 'parsedCookies' },
@@ -6,7 +6,6 @@ const { InputType } = require('@contrast/common');
6
6
  const { initAssessFixture } = require('@contrast/test/fixtures');
7
7
 
8
8
  describe('assess dataflow sources hapi', function () {
9
-
10
9
  const hapi = {
11
10
  server(server) {
12
11
  return server;
@@ -14,8 +14,10 @@
14
14
  */
15
15
 
16
16
  'use strict';
17
+
18
+ const { primordials: { StringPrototypeToLowerCase }, InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../constants');
17
20
  const { patchType } = require('../common');
18
- const { StringPrototypeToLowerCase, InputType } = require('@contrast/common');
19
21
 
20
22
  /**
21
23
  * @param {{
@@ -24,13 +26,15 @@ const { StringPrototypeToLowerCase, InputType } = require('@contrast/common');
24
26
  */
25
27
  module.exports = function (core) {
26
28
  const {
27
- assess: { dataflow, makeSourceContext },
29
+ assess: {
30
+ getSourceContext,
31
+ dataflow,
32
+ },
28
33
  instrumentation: { instrument },
29
34
  patcher,
30
- scopes,
31
35
  } = core;
32
36
 
33
- const logger = core.logger.child('contrast:assess');
37
+ const logger = core.logger.child({ name: 'contrast:assess' });
34
38
 
35
39
  /**
36
40
  * The around hook for `emit` that
@@ -43,16 +47,12 @@ module.exports = function (core) {
43
47
 
44
48
  try {
45
49
  const [, req, res] = data.args;
46
- const store = scopes.sources.getStore();
50
+ const sourceContext = getSourceContext(SOURCE);
47
51
 
48
- if (!store) {
49
- // this would indicate that sources did not install correctly
50
- throw new Error('async request store not found');
52
+ if (!sourceContext?.policy) {
53
+ return next();
51
54
  }
52
55
 
53
- store.assess = makeSourceContext(req, res);
54
- if (!store.assess) return;
55
-
56
56
  patcher.patch(res, 'writeHead', {
57
57
  name: 'write-head',
58
58
  patchType,
@@ -66,13 +66,13 @@ module.exports = function (core) {
66
66
  const value = obj[i + 1];
67
67
 
68
68
  if (StringPrototypeToLowerCase.call(key) === 'content-type') {
69
- store.assess.responseData.contentType = value;
69
+ sourceContext.responseData.contentType = value;
70
70
  }
71
71
  }
72
72
  } else if (typeof obj === 'object') {
73
73
  for (const [key, value] of Object.entries(obj)) {
74
74
  if (StringPrototypeToLowerCase.call(key) === 'content-type') {
75
- store.assess.responseData.contentType = value;
75
+ sourceContext.responseData.contentType = value;
76
76
  }
77
77
  }
78
78
  }
@@ -85,8 +85,12 @@ module.exports = function (core) {
85
85
  patchType,
86
86
  pre(data) {
87
87
  const [name = '', value] = data.args;
88
- if (StringPrototypeToLowerCase.call(name) === 'content-type' && scopes.sources.getStore()?.assess && value) {
89
- store.assess.responseData.contentType = value;
88
+ if (
89
+ value &&
90
+ StringPrototypeToLowerCase.call(name) === 'content-type' &&
91
+ getSourceContext(SOURCE)
92
+ ) {
93
+ sourceContext.responseData.contentType = value;
90
94
  }
91
95
  }
92
96
  });
@@ -99,7 +103,7 @@ module.exports = function (core) {
99
103
  constructorOpt: data.hooked,
100
104
  prependFrames: [data.orig]
101
105
  },
102
- sourceContext: store.assess
106
+ sourceContext,
103
107
  };
104
108
 
105
109
  // track the headers and the url.
@@ -4,6 +4,7 @@ const EventEmitter = require('events');
4
4
  const sinon = require('sinon');
5
5
  const { expect } = require('chai');
6
6
  const { initAssessFixture } = require('@contrast/test/fixtures');
7
+ const mocks = require('@contrast/test/mocks');
7
8
 
8
9
  describe('assess dataflow sources http', function () {
9
10
  let core, simulateRequestScope, request, response, server;
@@ -11,21 +12,9 @@ describe('assess dataflow sources http', function () {
11
12
  beforeEach(function () {
12
13
  ({ core, simulateRequestScope } = initAssessFixture());
13
14
 
14
- request = {
15
- url: 'http://host:8080/index.html?param=foo',
16
- httpVersion: 1,
17
- method: 'GET',
18
- socket: {
19
- remoteAddress: '127.0.0.1'
20
- },
21
- rawHeaders: ['Content-Type', 'application/json'],
22
- headers: {
23
- 'content-type': 'application/json'
24
- }
25
- };
26
- response = new EventEmitter();
27
- response.writeHead = function () { };
28
- response.setHeader = function () { };
15
+ request = mocks.incomingMessage();
16
+ response = mocks.serverResponse();
17
+
29
18
  class Server extends EventEmitter { }
30
19
  core.depHooks.resolve.withArgs({ name: 'http' }).yields({ Server });
31
20
  core.logger.child = () => core.logger;
@@ -36,6 +25,7 @@ describe('assess dataflow sources http', function () {
36
25
 
37
26
  it('instantiates assess store with appropriate metadata and handles base sources', function (next) {
38
27
  simulateRequestScope(() => {
28
+ core.scopes.sources.getStore().assess.req
39
29
  server.on('request', test);
40
30
  server.emit('request', request, response);
41
31
  });
@@ -44,17 +34,22 @@ describe('assess dataflow sources http', function () {
44
34
  const store = core.scopes.sources.getStore()?.assess;
45
35
  // validate store
46
36
  expect(store).to.have.property('propagationEventsCount', 0);
47
- // 3: url, headers, rawHeaders
48
- expect(store).to.have.property('sourceEventsCount', 3);
37
+ // url(1), headers(3), rawHeaders(3)
38
+ expect(store).to.have.property('sourceEventsCount', 7);
49
39
  expect(store.policy.enabledRules).to.be.a('Set').and.length.greaterThan(5);
40
+ // should match the mock
50
41
  expect(store.reqData).to.deep.equal({
51
42
  ip: '127.0.0.1',
52
- httpVersion: 1,
53
- method: 'GET',
54
- headers: { 'content-type': 'application/json' },
55
- uriPath: 'http://host:8080/index.html',
56
- queries: 'param=foo',
57
- contentType: 'application/json'
43
+ httpVersion: '1.1',
44
+ method: 'get',
45
+ headers: {
46
+ 'content-type': 'text/html',
47
+ language: 'en',
48
+ referer: 'http://fake.url.foo'
49
+ },
50
+ uriPath: '/index',
51
+ queries: '_id=123',
52
+ contentType: 'text/html'
58
53
  });
59
54
  expect(store.responseData).to.deep.equal({});
60
55
  // tracked inputs
@@ -101,11 +96,6 @@ describe('assess dataflow sources http', function () {
101
96
 
102
97
  function test(req, res) {
103
98
  const store = core.scopes.sources.getStore()?.assess;
104
- // logging
105
- expect(core.logger.error).to.have.been.calledWith(
106
- { err: sinon.match.has('message', 'async request store not found'), funcKey: sinon.match.string },
107
- 'Error during Assess request handling'
108
- );
109
99
  // validate store
110
100
  expect(store).to.be.undefined;
111
101
  // tracked inputs
@@ -135,12 +125,16 @@ describe('assess dataflow sources http', function () {
135
125
  expect(store.policy.enabledRules).to.be.a('Set').and.length.greaterThan(5);
136
126
  expect(store.reqData).to.deep.equal({
137
127
  ip: '127.0.0.1',
138
- httpVersion: 1,
139
- method: 'GET',
140
- headers: { 'content-type': 'application/json' },
141
- uriPath: 'http://host:8080/index.html',
142
- queries: 'param=foo',
143
- contentType: 'application/json'
128
+ httpVersion: '1.1',
129
+ method: 'get',
130
+ headers: {
131
+ 'content-type': 'text/html',
132
+ language: 'en',
133
+ referer: 'http://fake.url.foo'
134
+ },
135
+ uriPath: '/index',
136
+ queries: '_id=123',
137
+ contentType: 'text/html'
144
138
  });
145
139
  expect(store.responseData).to.deep.equal({});
146
140
  // tracked inputs
@@ -16,6 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
19
20
  const { patchType } = require('../../common');
20
21
 
21
22
  module.exports = (core) => {
@@ -23,7 +24,10 @@ module.exports = (core) => {
23
24
  depHooks,
24
25
  patcher,
25
26
  logger,
26
- assess: { dataflow: { sources } },
27
+ assess: {
28
+ getSourceContext,
29
+ dataflow: { sources }
30
+ },
27
31
  } = core;
28
32
 
29
33
  // Patch `koa-body` v4.x.x and `koa-bodyparser` v5.x.x packages
@@ -38,20 +42,16 @@ module.exports = (core) => {
38
42
  patchType,
39
43
  pre(data) {
40
44
  const { funcKey } = data;
41
- const sourceContext = core.scopes.sources.getStore()?.assess;
42
45
  const [ctx, origNext] = data.args;
46
+ const sourceContext = getSourceContext(SOURCE);
43
47
 
44
- if (!sourceContext) {
45
- logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
46
- return;
47
- }
48
+ if (!sourceContext) return;
48
49
 
49
50
  if (sourceContext.parsedBody) {
50
51
  logger.trace({ funcKey }, 'values already tracked');
51
52
  return;
52
53
  }
53
54
 
54
-
55
55
  data.args[1] = async function contrastNext(origErr) {
56
56
  const inputType = sourceContext.reqData.headers?.['content-type']?.includes('/json')
57
57
  ? InputType.JSON_VALUE
@@ -112,11 +112,11 @@ describe('assess dataflow sources koa-body v5.x.x and koa-bodyparser v4.x.x', fu
112
112
  });
113
113
  });
114
114
 
115
- it('does not call `.handle` when not in context', function () {
115
+ it('does not call `.handle` when there is no assess policy in request context', function () {
116
116
  simulateRequestScope(() => {
117
117
  const cParser = patchedKoaBodyparser(body);
118
118
  cParser({ request: {} }, nextStub);
119
- }, {});
119
+ }, { assess: { policy: null } });
120
120
 
121
121
  expect(sources.handle).not.to.have.been.called;
122
122
  expect(nextStub).to.have.been.calledOnce;
@@ -124,8 +124,7 @@ describe('assess dataflow sources koa-body v5.x.x and koa-bodyparser v4.x.x', fu
124
124
 
125
125
  it('does not call `.handle` when the values are already tracked', function () {
126
126
  simulateRequestScope(() => {
127
- const sourceContext = core.scopes.sources.getStore()?.assess;
128
- sourceContext.parsedBody = true;
127
+ core.scopes.sources.getStore().assess.parsedBody = true;
129
128
 
130
129
  const cParser = patchedKoaBodyparser(body);
131
130
  cParser({ request: {} }, nextStub);
@@ -14,20 +14,24 @@
14
14
  */
15
15
 
16
16
  'use strict';
17
- const { patchType } = require('../../common');
17
+
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
20
+ const { patchType } = require('../../common');
19
21
 
20
22
  module.exports = (core) => {
21
23
  const {
22
24
  depHooks,
23
25
  logger,
24
26
  patcher,
25
- scopes,
26
- assess: { dataflow: { sources } },
27
+ assess: {
28
+ getSourceContext,
29
+ dataflow: { sources }
30
+ },
27
31
  } = core;
28
32
 
29
33
  function handler(req, constructorOpt) {
30
- const sourceContext = scopes.sources.getStore()?.assess;
34
+ const sourceContext = getSourceContext(SOURCE);
31
35
  if (!sourceContext) return;
32
36
 
33
37
  function handle(context, data, key) {
@@ -16,6 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
19
20
  const { patchType } = require('../../common');
20
21
 
21
22
  module.exports = (core) => {
@@ -23,7 +24,10 @@ module.exports = (core) => {
23
24
  depHooks,
24
25
  patcher,
25
26
  logger,
26
- assess: { dataflow: { sources } },
27
+ assess: {
28
+ getSourceContext,
29
+ dataflow: { sources }
30
+ },
27
31
  } = core;
28
32
 
29
33
  // Patch `koa-router` and `@koa/router` to handle parsed params
@@ -36,13 +40,10 @@ module.exports = (core) => {
36
40
  name: `[${router}].layer.prototype`,
37
41
  patchType,
38
42
  post({ orig, hooked, result, name, funcKey }) {
39
- const sourceContext = core.scopes.sources.getStore()?.assess;
43
+ const sourceContext = getSourceContext(SOURCE);
40
44
  const inputType = InputType.URL_PARAMETER;
41
45
 
42
- if (!sourceContext) {
43
- logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
44
- return;
45
- }
46
+ if (!sourceContext) return;
46
47
 
47
48
  if (sourceContext.parsedParams) {
48
49
  logger.trace({ inputType, funcKey }, 'values already tracked');
@@ -97,12 +97,12 @@ describe('assess dataflow sources koa-router and @koa/router', function () {
97
97
  });
98
98
  });
99
99
 
100
- it(`[${router}] does not call \`.handle\` when there is no context`, function () {
100
+ it(`[${router}] does not call \`.handle\` when there is no assess policy in request context`, function () {
101
101
  const paramsFn = getParamsFn(router);
102
102
 
103
103
  simulateRequestScope(() => {
104
104
  paramsFn(params);
105
- }, {});
105
+ }, { assess: { policy: null } });
106
106
 
107
107
  expect(sources.handle).not.to.have.been.called;
108
108
  });
@@ -16,6 +16,8 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
+ const { InstrumentationType: { SOURCE } } = require('../../../../constants');
20
+
19
21
  const { patchType } = require('../../common');
20
22
 
21
23
  const inputType = InputType.QUERYSTRING;
@@ -30,7 +32,10 @@ module.exports = (core) => {
30
32
  logger,
31
33
  depHooks,
32
34
  patcher,
33
- assess: { dataflow: { sources } },
35
+ assess: {
36
+ getSourceContext,
37
+ dataflow: { sources }
38
+ },
34
39
  } = core;
35
40
 
36
41
  /**
@@ -40,10 +45,9 @@ module.exports = (core) => {
40
45
  depHooks.resolve({ name: 'koa', version: '>=2.3.0' }, (Koa) => {
41
46
  const createMiddleware = ({ name, funcKey }) => {
42
47
  const contrastStartMiddleware = function contrastStartMiddleware(ctx, next) {
43
- const sourceContext = core.scopes.sources.getStore()?.assess;
48
+ const sourceContext = getSourceContext(SOURCE);
44
49
 
45
50
  if (!sourceContext) {
46
- logger.error({ inputType, funcKey }, 'unable to handle Koa source. Missing `sourceContext`');
47
51
  return next();
48
52
  }
49
53
 
@@ -113,7 +113,7 @@ describe('assess dataflow koa v2.x', function () {
113
113
 
114
114
  simulateRequestScope(() => {
115
115
  contrastStartMiddleware({ query }, nextStub);
116
- }, {});
116
+ }, { assess: { policy: null } });
117
117
 
118
118
  expect(nextStub).to.have.been.calledOnce;
119
119
  expect(sources.handle).not.to.have.been.called;