@contrast/assess 1.35.0 → 1.37.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 (169) 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 +99 -5
  38. package/lib/dataflow/propagation/install/mongoose/schema-string.test.js +41 -2
  39. package/lib/dataflow/propagation/install/mustache-escape.js +5 -8
  40. package/lib/dataflow/propagation/install/mustache-escape.test.js +2 -2
  41. package/lib/dataflow/propagation/install/mysql-connection-escape.js +5 -8
  42. package/lib/dataflow/propagation/install/mysql-connection-escape.test.js +2 -2
  43. package/lib/dataflow/propagation/install/parse-int.js +3 -3
  44. package/lib/dataflow/propagation/install/path/basename.js +7 -12
  45. package/lib/dataflow/propagation/install/path/basename.test.js +2 -2
  46. package/lib/dataflow/propagation/install/path/common.js +2 -2
  47. package/lib/dataflow/propagation/install/path/dirname.js +5 -10
  48. package/lib/dataflow/propagation/install/path/dirname.test.js +2 -2
  49. package/lib/dataflow/propagation/install/path/extname.js +6 -11
  50. package/lib/dataflow/propagation/install/path/extname.test.js +2 -2
  51. package/lib/dataflow/propagation/install/path/format.js +7 -13
  52. package/lib/dataflow/propagation/install/path/format.test.js +2 -2
  53. package/lib/dataflow/propagation/install/path/join-and-resolve.js +7 -12
  54. package/lib/dataflow/propagation/install/path/join-and-resolve.test.js +2 -2
  55. package/lib/dataflow/propagation/install/path/normalize.js +4 -11
  56. package/lib/dataflow/propagation/install/path/normalize.test.js +2 -2
  57. package/lib/dataflow/propagation/install/path/parse.js +3 -8
  58. package/lib/dataflow/propagation/install/path/parse.test.js +2 -2
  59. package/lib/dataflow/propagation/install/path/relative.js +5 -11
  60. package/lib/dataflow/propagation/install/path/relative.test.js +2 -2
  61. package/lib/dataflow/propagation/install/path/toNamespacedPath.js +5 -11
  62. package/lib/dataflow/propagation/install/path/toNamespacedPath.test.js +2 -2
  63. package/lib/dataflow/propagation/install/pug/index.js +8 -3
  64. package/lib/dataflow/propagation/install/pug-runtime-escape.js +5 -8
  65. package/lib/dataflow/propagation/install/pug-runtime-escape.test.js +1 -1
  66. package/lib/dataflow/propagation/install/querystring/escape.js +3 -3
  67. package/lib/dataflow/propagation/install/querystring/parse.js +7 -11
  68. package/lib/dataflow/propagation/install/querystring/stringify.js +3 -3
  69. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +4 -3
  70. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.test.js +5 -3
  71. package/lib/dataflow/propagation/install/send.js +5 -10
  72. package/lib/dataflow/propagation/install/sequelize/query-generator.js +3 -4
  73. package/lib/dataflow/propagation/install/sequelize/sql-string.js +8 -12
  74. package/lib/dataflow/propagation/install/sequelize/sql-string.test.js +2 -13
  75. package/lib/dataflow/propagation/install/sql-template-strings.js +3 -5
  76. package/lib/dataflow/propagation/install/sql-template-strings.test.js +2 -2
  77. package/lib/dataflow/propagation/install/string/concat.js +2 -1
  78. package/lib/dataflow/propagation/install/string/concat.test.js +15 -2
  79. package/lib/dataflow/propagation/install/string/format-methods.js +4 -2
  80. package/lib/dataflow/propagation/install/string/format-methods.test.js +15 -2
  81. package/lib/dataflow/propagation/install/string/html-methods.js +1 -1
  82. package/lib/dataflow/propagation/install/string/html-methods.test.js +15 -2
  83. package/lib/dataflow/propagation/install/string/index.js +2 -2
  84. package/lib/dataflow/propagation/install/string/match-all.js +2 -1
  85. package/lib/dataflow/propagation/install/string/match-all.test.js +13 -0
  86. package/lib/dataflow/propagation/install/string/match.js +11 -10
  87. package/lib/dataflow/propagation/install/string/match.test.js +13 -0
  88. package/lib/dataflow/propagation/install/string/replace.js +15 -9
  89. package/lib/dataflow/propagation/install/string/replace.test.js +13 -0
  90. package/lib/dataflow/propagation/install/string/slice.js +2 -1
  91. package/lib/dataflow/propagation/install/string/slice.test.js +13 -0
  92. package/lib/dataflow/propagation/install/string/split.js +2 -1
  93. package/lib/dataflow/propagation/install/string/split.test.js +13 -0
  94. package/lib/dataflow/propagation/install/string/substring.js +2 -1
  95. package/lib/dataflow/propagation/install/string/substring.test.js +13 -0
  96. package/lib/dataflow/propagation/install/string/trim.js +4 -1
  97. package/lib/dataflow/propagation/install/string/trim.test.js +13 -0
  98. package/lib/dataflow/propagation/install/unescape.js +5 -8
  99. package/lib/dataflow/propagation/install/unescape.test.js +2 -2
  100. package/lib/dataflow/propagation/install/url/domain-parsers.js +4 -5
  101. package/lib/dataflow/propagation/install/url/domain-parsers.test.js +2 -2
  102. package/lib/dataflow/propagation/install/url/parse.js +3 -2
  103. package/lib/dataflow/propagation/install/url/parse.test.js +2 -2
  104. package/lib/dataflow/propagation/install/url/searchParams.js +5 -5
  105. package/lib/dataflow/propagation/install/url/searchParams.test.js +2 -2
  106. package/lib/dataflow/propagation/install/url/url.js +6 -3
  107. package/lib/dataflow/propagation/install/url/url.test.js +2 -2
  108. package/lib/dataflow/propagation/install/util-format.js +7 -6
  109. package/lib/dataflow/propagation/install/util-format.test.js +2 -2
  110. package/lib/dataflow/propagation/install/validator/hooks.js +7 -2
  111. package/lib/dataflow/sinks/install/child-process.js +1 -1
  112. package/lib/dataflow/sinks/install/child-process.test.js +1 -1
  113. package/lib/dataflow/sinks/install/fs.js +1 -1
  114. package/lib/dataflow/sinks/install/fs.test.js +1 -1
  115. package/lib/dataflow/sinks/install/function.js +1 -1
  116. package/lib/dataflow/sinks/install/http/request.js +2 -1
  117. package/lib/dataflow/sinks/install/http/request.test.js +1 -1
  118. package/lib/dataflow/sinks/install/http/server-response.test.js +3 -5
  119. package/lib/dataflow/sinks/install/restify.js +1 -1
  120. package/lib/dataflow/sinks/install/vm.js +4 -2
  121. package/lib/dataflow/sinks/install/vm.test.js +1 -1
  122. package/lib/dataflow/sources/handler.js +5 -2
  123. package/lib/dataflow/sources/install/body-parser1.test.js +4 -4
  124. package/lib/dataflow/sources/install/busboy.js +8 -3
  125. package/lib/dataflow/sources/install/busboy.test.js +2 -2
  126. package/lib/dataflow/sources/install/cookie-parser1.test.js +2 -2
  127. package/lib/dataflow/sources/install/express/params.js +14 -11
  128. package/lib/dataflow/sources/install/express/params.test.js +5 -7
  129. package/lib/dataflow/sources/install/express/parsedUrl.js +3 -2
  130. package/lib/dataflow/sources/install/fastify/fastify.js +7 -6
  131. package/lib/dataflow/sources/install/fastify/fastify.test.js +2 -2
  132. package/lib/dataflow/sources/install/formidable1.js +7 -6
  133. package/lib/dataflow/sources/install/formidable1.test.js +2 -2
  134. package/lib/dataflow/sources/install/hapi/hapi.js +8 -10
  135. package/lib/dataflow/sources/install/hapi/hapi.test.js +0 -1
  136. package/lib/dataflow/sources/install/http.js +20 -16
  137. package/lib/dataflow/sources/install/http.test.js +28 -34
  138. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +7 -7
  139. package/lib/dataflow/sources/install/koa/koa-bodyparsers.test.js +3 -4
  140. package/lib/dataflow/sources/install/koa/koa-multer.js +8 -4
  141. package/lib/dataflow/sources/install/koa/koa-routers.js +7 -6
  142. package/lib/dataflow/sources/install/koa/koa-routers.test.js +2 -2
  143. package/lib/dataflow/sources/install/koa/koa2.js +7 -3
  144. package/lib/dataflow/sources/install/koa/koa2.test.js +1 -1
  145. package/lib/dataflow/sources/install/multer1.js +6 -2
  146. package/lib/dataflow/sources/install/qs6.js +1 -1
  147. package/lib/dataflow/sources/install/querystring.js +1 -1
  148. package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.js +1 -4
  149. package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.test.js +6 -8
  150. package/lib/dataflow/sources/install/restify/jsonBodyParser.js +0 -1
  151. package/lib/dataflow/sources/install/restify/jsonBodyParser.test.js +4 -8
  152. package/lib/dataflow/sources/install/restify/router.test.js +2 -2
  153. package/lib/dataflow/tag-utils.js +1 -1
  154. package/lib/dataflow/tracker.js +1 -1
  155. package/lib/dataflow/utils/is-safe-content-type.js +3 -2
  156. package/lib/event-factory.js +4 -4
  157. package/lib/get-policy.js +2 -2
  158. package/lib/index.js +18 -7
  159. package/lib/index.test.js +4 -0
  160. package/lib/make-source-context.js +37 -28
  161. package/lib/make-source-context.test.js +7 -7
  162. package/lib/response-scanning/handlers/index.js +7 -5
  163. package/lib/response-scanning/handlers/utils.js +11 -8
  164. package/lib/response-scanning/install/http.js +1 -1
  165. package/lib/sampler.js +136 -0
  166. package/lib/sampler.test.js +296 -0
  167. package/lib/session-configuration/install/express-session.js +1 -1
  168. package/lib/session-configuration/install/fastify-cookie.js +1 -1
  169. package/package.json +10 -10
@@ -5,7 +5,7 @@ const { expect } = require('chai');
5
5
  const {
6
6
  DataflowTag: { UNTRUSTED, CUSTOM_VALIDATED },
7
7
  Rule: { UNSAFE_CODE_EXECUTION },
8
- UtilInspect,
8
+ primordials: { UtilInspect },
9
9
  } = require('@contrast/common');
10
10
  const { initAssessFixture } = require('@contrast/test/fixtures');
11
11
 
@@ -19,7 +19,10 @@ const {
19
19
  InputType,
20
20
  DataflowTag,
21
21
  isString,
22
- ArrayPrototypeJoin,
22
+ primordials: {
23
+ ArrayPrototypeJoin,
24
+ StringPrototypeToLowerCase
25
+ }
23
26
  } = require('@contrast/common');
24
27
 
25
28
  module.exports = function (core) {
@@ -55,7 +58,7 @@ module.exports = function (core) {
55
58
  }
56
59
  }
57
60
 
58
- if (inputType === InputType.HEADER && fieldName.toLowerCase() === 'referer') {
61
+ if (inputType === InputType.HEADER && StringPrototypeToLowerCase.call(fieldName) === 'referer') {
59
62
  tags[DataflowTag.HEADER] = [0, stop];
60
63
  }
61
64
 
@@ -105,7 +105,7 @@ describe('assess dataflow sources body-parser v1', function () {
105
105
  });
106
106
  });
107
107
 
108
- it('does not handle the request when not in context', function () {
108
+ it('does not handle the request when there is no assess policy in request context', function () {
109
109
  const middleware = bodyParser('text');
110
110
 
111
111
  simulateRequestScope(() => {
@@ -116,7 +116,7 @@ describe('assess dataflow sources body-parser v1', function () {
116
116
  { funcKey: 'assess-dataflow-source:body-parser.bodyParser' },
117
117
  'unable to handle source. Missing `sourceContext`'
118
118
  );
119
- }, {});
119
+ }, { assess: { policy: null } });
120
120
  });
121
121
 
122
122
  it('does not handle the request when already tracked', function () {
@@ -205,7 +205,7 @@ describe('assess dataflow sources body-parser v1', function () {
205
205
  });
206
206
  });
207
207
 
208
- it('does not handle the request when not in context', function () {
208
+ it('does not handle the request when there is in context', function () {
209
209
  simulateRequestScope(() => {
210
210
  middleware(req, res, next);
211
211
 
@@ -214,7 +214,7 @@ describe('assess dataflow sources body-parser v1', function () {
214
214
  { funcKey: `assess-dataflow-source:body-parser.${method}.${method}Parser` },
215
215
  'unable to handle source. Missing `sourceContext`',
216
216
  );
217
- }, {});
217
+ }, { assess: { policy: null } });
218
218
  });
219
219
 
220
220
  it('does not handle the request when already tracked', function () {
@@ -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;
@@ -26,16 +27,20 @@ module.exports = (core) => {
26
27
  depHooks,
27
28
  patcher,
28
29
  logger,
29
- assess: { dataflow: { sources } },
30
+ assess: {
31
+ getSourceContext,
32
+ dataflow: {
33
+ sources,
34
+ }
35
+ },
30
36
  } = core;
31
37
 
32
38
  function createPreHook(finalEventName) {
33
39
  return function (data) {
34
40
  const { orig, hooked, funcKey } = data;
35
- const sourceContext = core.scopes.sources.getStore()?.assess;
41
+ const sourceContext = getSourceContext(SOURCE);
36
42
 
37
43
  if (!sourceContext) {
38
- logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
39
44
  return;
40
45
  }
41
46
 
@@ -110,12 +110,12 @@ describe('assess dataflow sources busboy', function () {
110
110
  );
111
111
  });
112
112
 
113
- it('does not call `.handle` when not in context', function () {
113
+ it('does not call `.handle` when there is no assess policy in request context', function () {
114
114
  simulateRequestScope(() => {
115
115
  emitMethodVersions[version]('field', 'field1', 'value1');
116
116
  emitMethodVersions[version]('field', 'field2', 'value2');
117
117
  emitMethodVersions[version](finalEvent);
118
- }, {});
118
+ }, { assess: { policy: null } });
119
119
 
120
120
  expect(sources.handle).not.to.have.been.called;
121
121
  });
@@ -67,7 +67,7 @@ describe('assess dataflow sources cookie-parser v1', function () {
67
67
  });
68
68
  });
69
69
 
70
- it('does not handle the request when not in context', function () {
70
+ it('does not handle when there is no assess policy in request context', function () {
71
71
  const middleware = cookieParser();
72
72
 
73
73
  simulateRequestScope(() => {
@@ -78,7 +78,7 @@ describe('assess dataflow sources cookie-parser v1', function () {
78
78
  { funcKey: 'assess-dataflow-source:cookie-parser.cookieParser' },
79
79
  'unable to handle source. Missing `sourceContext`',
80
80
  );
81
- }, {});
81
+ }, { assess: { policy: null } });
82
82
  });
83
83
 
84
84
  it('does not handle the request when cookies already tracked', function () {
@@ -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);