@contrast/assess 1.34.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 (171) 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 +6 -3
  122. package/lib/dataflow/sources/handler.test.js +38 -0
  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/event-factory.test.js +19 -14
  158. package/lib/get-policy.js +2 -2
  159. package/lib/index.d.ts +11 -6
  160. package/lib/index.js +18 -7
  161. package/lib/index.test.js +4 -0
  162. package/lib/make-source-context.js +37 -28
  163. package/lib/make-source-context.test.js +7 -7
  164. package/lib/response-scanning/handlers/index.js +7 -5
  165. package/lib/response-scanning/handlers/utils.js +11 -8
  166. package/lib/response-scanning/install/http.js +1 -1
  167. package/lib/sampler.js +136 -0
  168. package/lib/sampler.test.js +296 -0
  169. package/lib/session-configuration/install/express-session.js +1 -1
  170. package/lib/session-configuration/install/fastify-cookie.js +1 -1
  171. package/package.json +10 -10
@@ -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,12 +24,15 @@ 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
  scopes,
28
32
  } = core;
29
33
 
30
34
  function handler(req, constructorOpt) {
31
- const sourceContext = scopes.sources.getStore()?.assess;
35
+ const sourceContext = getSourceContext(SOURCE);
32
36
  if (!sourceContext) return;
33
37
 
34
38
  function handle(context, data, key) {
@@ -16,8 +16,8 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType: { QUERYSTRING: inputType } } = require('@contrast/common');
19
- const { patchType } = require('../common');
20
19
  const { InstrumentationType: { SOURCE } } = require('../../../constants');
20
+ const { patchType } = require('../common');
21
21
 
22
22
  module.exports = (core) => {
23
23
  const {
@@ -16,8 +16,8 @@
16
16
  'use strict';
17
17
 
18
18
  const { InputType } = require('@contrast/common');
19
- const { patchType } = require('../common');
20
19
  const { InstrumentationType: { SOURCE } } = require('../../../constants');
20
+ const { patchType } = require('../common');
21
21
 
22
22
  module.exports = (core) => {
23
23
  const {
@@ -46,10 +46,7 @@ module.exports = function init(core) {
46
46
  data.args[2] = function contrastNext(...args) {
47
47
  const sourceContext = getSourceContext(SOURCE);
48
48
 
49
- if (!sourceContext) {
50
- logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
51
- return next(...args);
52
- }
49
+ if (!sourceContext) return next(...args);
53
50
 
54
51
  if (sourceContext.parsedBody) {
55
52
  logger.trace({ funcKey }, 'values already tracked');
@@ -29,20 +29,18 @@ describe('assess dataflow sources restify fieldedTextBodyParser', function () {
29
29
  middleware = fieldedTextBodyParserStub();
30
30
  });
31
31
 
32
- it('does not call `handle` when not in context', function () {
32
+ it('does not call `handle` when there is no assess policy in request context', function () {
33
33
  simulateRequestScope(() => {
34
34
  middleware(req, res, next);
35
35
 
36
36
  expect(handle).not.to.have.been.called;
37
- expect(core.logger.error).to.have.been.calledWith(
38
- { funcKey: 'assess-dataflow-source:restify.plugins.fieldedTextBodyParser.parseFieldedText' },
39
- 'unable to handle source. Missing `sourceContext`'
40
- );
41
- }, {});
37
+ }, { assess: { policy: null } });
42
38
  });
43
39
 
44
- it('does not call `handle` when body is already tracked', function () {
40
+ it('does not handle source when body is already tracked', function () {
45
41
  simulateRequestScope(() => {
42
+ core.scopes.sources.getStore().assess.parsedBody = true;
43
+
46
44
  middleware(req, res, next);
47
45
 
48
46
  expect(handle).not.to.have.been.called;
@@ -50,7 +48,7 @@ describe('assess dataflow sources restify fieldedTextBodyParser', function () {
50
48
  { funcKey: 'assess-dataflow-source:restify.plugins.fieldedTextBodyParser.parseFieldedText' },
51
49
  'values already tracked'
52
50
  );
53
- }, { assess: { parsedBody: true } });
51
+ });
54
52
  });
55
53
 
56
54
  it('calls `handle` appropriately', function () {
@@ -51,7 +51,6 @@ module.exports = function init(core) {
51
51
  const sourceContext = getSourceContext(SOURCE);
52
52
 
53
53
  if (!sourceContext) {
54
- logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
55
54
  return next(...args);
56
55
  }
57
56
 
@@ -31,24 +31,20 @@ describe('assess dataflow sources restify jsonBodyParser', function () {
31
31
  init(core).install();
32
32
  });
33
33
 
34
- it('does not call `handle` when not in context', function () {
34
+ it('does not handle source when there is no assess policy in request context', function () {
35
35
  const [, middleware] = jsonBodyParserStub();
36
36
 
37
37
  simulateRequestScope(() => {
38
38
  middleware(req, res, next);
39
-
40
39
  expect(handle).not.to.have.been.called;
41
- expect(core.logger.error).to.have.been.calledWith(
42
- { funcKey: 'assess-dataflow-source:restify.plugins.jsonBodyParser.parseJson' },
43
- 'unable to handle source. Missing `sourceContext`'
44
- );
45
- }, {});
40
+ }, { assess: { policy: null } });
46
41
  });
47
42
 
48
43
  it('does not call `handle` when body is already tracked', function () {
49
44
  const [, middleware] = jsonBodyParserStub();
50
45
 
51
46
  simulateRequestScope(() => {
47
+ core.scopes.sources.getStore().assess.parsedBody = true;
52
48
  middleware(req, res, next);
53
49
 
54
50
  expect(handle).not.to.have.been.called;
@@ -56,7 +52,7 @@ describe('assess dataflow sources restify jsonBodyParser', function () {
56
52
  { funcKey: 'assess-dataflow-source:restify.plugins.jsonBodyParser.parseJson' },
57
53
  'values already tracked'
58
54
  );
59
- }, { assess: { parsedBody: true } });
55
+ });
60
56
  });
61
57
 
62
58
  it('calls `handle` appropriately', function () {
@@ -23,7 +23,7 @@ describe('assess dataflow sources restify router', function () {
23
23
  init(core).install();
24
24
  });
25
25
 
26
- it('does not call `handle` when not in context', function () {
26
+ it('does not call `handle` when there is no assess policy in request context', function () {
27
27
  simulateRequestScope(() => {
28
28
  Router.prototype.lookup(req);
29
29
 
@@ -32,7 +32,7 @@ describe('assess dataflow sources restify router', function () {
32
32
  { funcKey: 'assess-dataflow-source:restify.Router.prototype.lookup' },
33
33
  'unable to handle source. Missing `sourceContext`'
34
34
  );
35
- }, {});
35
+ }, { assess: { policy: null } });
36
36
  });
37
37
 
38
38
  it('does not call `handle` when body is already tracked', function () {
@@ -14,7 +14,7 @@
14
14
  */
15
15
  'use strict';
16
16
 
17
- const { StringPrototypeSplit } = require('@contrast/common');
17
+ const { primordials: { StringPrototypeSplit } } = require('@contrast/common');
18
18
 
19
19
  //
20
20
  // This module implements tag range manipulation functions. There are generally
@@ -16,7 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const distringuish = require('@contrast/distringuish');
19
- const { BufferPrototypeToString, BufferFrom, isString } = require('@contrast/common');
19
+ const { primordials: { BufferPrototypeToString, BufferFrom }, isString } = require('@contrast/common');
20
20
 
21
21
  module.exports = function tracker(core) {
22
22
  const {
@@ -13,7 +13,7 @@
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
15
  'use strict';
16
-
16
+ const { primordials: { ArrayPrototypeJoin, RegExpPrototypeTest } } = require('@contrast/common');
17
17
  const SAFE_XSS_CONTENT_TYPES = [
18
18
  '/json',
19
19
  '/x-json',
@@ -22,9 +22,10 @@ const SAFE_XSS_CONTENT_TYPES = [
22
22
  '/pdf',
23
23
  '/csv'
24
24
  ];
25
+ const SAFE_XSS_REGEX = new RegExp(ArrayPrototypeJoin.call(SAFE_XSS_CONTENT_TYPES, '|'));
25
26
 
26
27
  function isSafeContentType(contentType) {
27
- return new RegExp(SAFE_XSS_CONTENT_TYPES.join('|')).test(contentType);
28
+ return RegExpPrototypeTest.call(SAFE_XSS_REGEX, contentType);
28
29
  }
29
30
 
30
31
  module.exports = { isSafeContentType, SAFE_XSS_CONTENT_TYPES };
@@ -15,7 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { InputType, StringPrototypeMatch } = require('@contrast/common');
18
+ const { InputType, primordials: { StringPrototypeMatch } } = require('@contrast/common');
19
19
  const ANNOTATION_REGEX = /^(A|O|R|P|P\d+)$/;
20
20
  const SOURCE_EVENT_MSG = 'Source event not created: %s';
21
21
  const PROPAGATION_EVENT_MSG = 'Propagation event not created: %s';
@@ -183,7 +183,7 @@ module.exports = function (core) {
183
183
  return null;
184
184
  }
185
185
  if (
186
- (!source || !source.match(ANNOTATION_REGEX))
186
+ (!source || !StringPrototypeMatch.call(source, ANNOTATION_REGEX))
187
187
  ) {
188
188
  logger.debug({ name }, 'malformed or missing sink event source field');
189
189
  return null;
@@ -237,7 +237,7 @@ module.exports = function (core) {
237
237
  }
238
238
 
239
239
  if (
240
- (!source || !source.match(ANNOTATION_REGEX))
240
+ (!source || !StringPrototypeMatch.call(source, ANNOTATION_REGEX))
241
241
  ) {
242
242
  logger.debug({ name }, 'malformed or missing sink event source field');
243
243
  return null;
@@ -299,7 +299,7 @@ module.exports = function (core) {
299
299
  return null;
300
300
  }
301
301
 
302
- if (!source || !source.match(ANNOTATION_REGEX)) {
302
+ if (!source || !StringPrototypeMatch.call(source, ANNOTATION_REGEX)) {
303
303
  logger.debug({ name }, 'malformed or missing sink event source field');
304
304
  return null;
305
305
  }
@@ -199,14 +199,16 @@ testMethod('assess event-factory', function () {
199
199
  });
200
200
  });
201
201
 
202
- it('returns an event without stacktrace generator function when stacktraces option is not set to "ALL"', function () {
203
- core.config.assess.stacktraces = 'SOME';
204
- core.scopes.sources.run(validStore, function () {
205
- const result = createPropagationEvent(validData);
202
+ ['SOME', 'SINK', 'NONE'].forEach((option) => {
203
+ it(`returns an event without stacktrace generator function when stacktraces option is not set to "ALL" and set to ${option}`, function () {
204
+ core.config.assess.stacktraces = option;
205
+ core.scopes.sources.run(validStore, function () {
206
+ const result = createPropagationEvent(validData);
206
207
 
207
- expect(result).to.be.like(validResult);
208
- expect(result.time).not.to.be.undefined;
209
- expect(result.stack).to.deep.equal([]);
208
+ expect(result).to.be.like(validResult);
209
+ expect(result.time).not.to.be.undefined;
210
+ expect(result.stack).to.deep.equal([]);
211
+ });
210
212
  });
211
213
  });
212
214
  });
@@ -296,14 +298,17 @@ testMethod('assess event-factory', function () {
296
298
  });
297
299
  });
298
300
 
299
- it('returns an event with stacktrace generator function when stacktraces option is not set to "NONE"', function () {
300
- core.config.assess.stacktraces = 'ALL';
301
- core.scopes.sources.run(validStore, function () {
302
- const result = createSinkEvent(validData);
301
+ ['ALL', 'SOME', 'SINK'].forEach((option) => {
302
+ it(`returns an event with stacktrace generator function when stacktraces option is not set to "NONE" and set to ${option}`, function () {
303
+ core.config.assess.stacktraces = option;
304
+ core.scopes.sources.run(validStore, function () {
305
+ const result = createSinkEvent(validData);
303
306
 
304
- expect(result).to.be.like(validResult);
305
- expect(result.time).not.to.be.undefined;
306
- expect(result.stack).to.be.instanceOf(Array);
307
+ expect(result).to.be.like(validResult);
308
+ expect(result.time).not.to.be.undefined;
309
+ expect(result.stack).to.be.instanceOf(Array);
310
+ expect(result.stack.length).to.be.greaterThan(0);
311
+ });
307
312
  });
308
313
  });
309
314
 
package/lib/get-policy.js CHANGED
@@ -22,7 +22,7 @@ const {
22
22
  Rule,
23
23
  ResponseScanningRule,
24
24
  SessionConfigurationRule,
25
- ArrayPrototypeJoin,
25
+ primordials: { ArrayPrototypeJoin, RegExpPrototypeTest }
26
26
  } = require('@contrast/common');
27
27
 
28
28
  const ASSESS_RULES = Object.values({
@@ -323,7 +323,7 @@ class InputExclusion extends UrlExclusion {
323
323
  matchesInputName(name) {
324
324
  // BODY and QUERYSTRING always match since they apply broadly
325
325
  if (!this._inputName && !this._inputNameRegex) return true;
326
- return this._inputNameRegex ? this._inputNameRegex.test(name) : this._inputName === name;
326
+ return this._inputNameRegex ? RegExpPrototypeTest.call(this._inputNameRegex, name) : this._inputName === name;
327
327
  }
328
328
  }
329
329
 
package/lib/index.d.ts CHANGED
@@ -12,17 +12,22 @@
12
12
  * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
- import { IncomingMessage, ServerResponse } from 'node:http';
16
- import {
17
- Rule,
18
- SessionConfigurationRule,
19
- } from '@contrast/common';
15
+ import { Rule, SessionConfigurationRule } from '@contrast/common';
16
+ import { Config } from '@contrast/config';
20
17
  import { Core as _Core } from '@contrast/core';
18
+ import { Deadzones } from '@contrast/deadzones';
19
+ import { DepHooks } from '@contrast/dep-hooks';
20
+ import { Logger } from '@contrast/logger';
21
+ import { Patcher } from '@contrast/patcher';
22
+ import { ReporterBus } from '@contrast/reporter';
23
+ import { Rewriter } from '@contrast/rewriter';
24
+ import { Scopes } from '@contrast/scopes';
25
+ import { IncomingMessage, ServerResponse } from 'node:http';
21
26
 
22
27
  export interface Core extends _Core {
23
28
  config: Config;
24
29
  logger: Logger;
25
- depHooks: RequireHook;
30
+ depHooks: DepHooks;
26
31
  patcher: Patcher
27
32
  rewriter: Rewriter;
28
33
  scopes: Scopes;
package/lib/index.js CHANGED
@@ -19,16 +19,22 @@ const { inspect } = require('util');
19
19
  const { callChildComponentMethodsSync } = require('@contrast/common');
20
20
 
21
21
  module.exports = function assess(core) {
22
- const { scopes: { instrumentation } } = core;
22
+ const {
23
+ config,
24
+ sources,
25
+ scopes: { instrumentation }
26
+ } = core;
23
27
 
24
28
  const assess = core.assess = {
25
29
  install() {
26
- if (!core.config.getEffectiveValue('assess.enable')) {
30
+ if (!config.getEffectiveValue('assess.enable')) {
27
31
  core.logger.debug('assess is disabled, skipping installation');
28
32
  return;
29
33
  }
30
34
 
35
+ // configure rewriter
31
36
  core.rewriter.install('assess');
37
+ // dispatch subcomponent installation
32
38
  callChildComponentMethodsSync(core.assess, 'install');
33
39
  },
34
40
  };
@@ -43,19 +49,24 @@ module.exports = function assess(core) {
43
49
  instrumentation.run(store, inspect, val, opts);
44
50
  };
45
51
 
46
- require('./rule-scopes')(core);
52
+ // ancillary tools used by different features
53
+ require('./sampler')(core);
47
54
  require('./get-policy')(core);
48
55
  require('./make-source-context')(core);
56
+ require('./rule-scopes')(core);
49
57
  require('./get-source-context')(core);
50
58
  require('./event-factory')(core);
51
- require('./crypto-analysis')(core);
59
+
60
+ // various Assess features
52
61
  require('./dataflow')(core);
62
+ require('./crypto-analysis')(core);
53
63
  require('./response-scanning')(core);
54
64
  require('./session-configuration')(core);
55
65
 
56
- // todo
57
- // crypto rule implementations
58
- // static rule implementations in coordination with (CLI) rewriter
66
+ // append async state to sources store when request-scope sources are created
67
+ sources.addHook('onSource', (ctx) => {
68
+ ctx.store.assess = assess.makeSourceContext(ctx);
69
+ });
59
70
 
60
71
  return assess;
61
72
  };
package/lib/index.test.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { expect } = require('chai');
4
+ const sinon = require('sinon');
4
5
  const proxyquire = require('proxyquire');
5
6
  const mocks = require('@contrast/test/mocks');
6
7
 
@@ -19,6 +20,9 @@ describe('assess', function () {
19
20
  core.rewriter = mocks.rewriter();
20
21
  core.scopes = mocks.scopes();
21
22
  core.config.assess.enable = true;
23
+ core.sources = {
24
+ addHook: sinon.stub(),
25
+ };
22
26
 
23
27
  assessModule = proxyquire('.', {
24
28
  './session-configuration': moduleMock('sessionConfiguration', assessMock),
@@ -15,8 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { StringPrototypeToLowerCase } = require('@contrast/common');
19
-
18
+ const { primordials: { StringPrototypeToLowerCase, StringPrototypeSlice } } = require('@contrast/common');
20
19
  /**
21
20
  * @param {{
22
21
  * assess: import('@contrast/assess').Assess,
@@ -24,46 +23,56 @@ const { StringPrototypeToLowerCase } = require('@contrast/common');
24
23
  * }} core
25
24
  */
26
25
  module.exports = function(core) {
27
- const {
28
- assess: { getPolicy },
29
- logger,
30
- } = core;
26
+ const { assess, logger } = core;
31
27
 
32
28
  /**
33
29
  * @returns {import('@contrast/assess').SourceContext}
34
30
  */
35
- return core.assess.makeSourceContext = function (req, res) {
36
- let contentType, queries, uriPath;
37
-
31
+ return core.assess.makeSourceContext = function (sourceData) {
38
32
  try {
39
- const ix = req.url.indexOf('?');
40
- if (ix >= 0) {
41
- uriPath = req.url.slice(0, ix);
42
- queries = req.url.slice(ix + 1);
33
+ // todo: how to handle non-HTTP sources
34
+ const { incomingMessage: req } = sourceData;
35
+
36
+ // minimally process the request data for sampling and exclusions.
37
+ // more request fields will be appended in final result below.
38
+ let uriPath;
39
+ let queries;
40
+ const idx = req.url.indexOf('?');
41
+ if (idx >= 0) {
42
+ uriPath = StringPrototypeSlice.call(req.url, 0, idx);
43
+ queries = StringPrototypeSlice.call(req.url, idx + 1);
43
44
  } else {
44
45
  uriPath = req.url;
45
46
  queries = '';
46
47
  }
47
48
 
48
- // copy to avoid storing tracked values
49
- const headers = { ...req.headers };
50
- if (headers['content-type']) {
51
- contentType = StringPrototypeToLowerCase.call(headers['content-type']);
52
- }
49
+ const ctx = sourceData.store.assess = {
50
+ // default policy to `null` until it is set later below. this will cause
51
+ // all instrumentation to short-circuit, see `./get-source-context.js`.
52
+ policy: null,
53
+ reqData: { uriPath, queries },
54
+ };
55
+
56
+ // check whether sampling allows processing
57
+ ctx.sampleInfo = assess.sampler?.getSampleInfo?.(sourceData) ?? null;
58
+ if (ctx.sampleInfo?.canSample === false) return ctx;
59
+
60
+ // set policy - can be returned as `null` if request is url-excluded.
61
+ ctx.policy = assess.getPolicy(ctx.reqData);
62
+ if (!ctx.policy) return ctx;
63
+
64
+ // build remaining reqData
65
+ ctx.reqData.headers = { ...req.headers }; // copy to avoid storing tracked values
66
+ ctx.reqData.ip = req.socket.remoteAddress;
67
+ ctx.reqData.httpVersion = req.httpVersion;
68
+ ctx.reqData.method = req.method;
69
+ if (ctx.reqData.headers['content-type'])
70
+ ctx.reqData.contentType = StringPrototypeToLowerCase.call(ctx.reqData.headers['content-type']);
53
71
 
54
72
  return {
73
+ ...ctx,
55
74
  propagationEventsCount: 0,
56
75
  sourceEventsCount: 0,
57
- policy: getPolicy({ uriPath }),
58
- reqData: {
59
- ip: req.socket.remoteAddress,
60
- httpVersion: req.httpVersion,
61
- method: req.method,
62
- headers,
63
- uriPath,
64
- queries,
65
- contentType,
66
- },
67
76
  responseData: {},
68
77
  ruleState: {},
69
78
  };
@@ -5,7 +5,6 @@ const sinon = require('sinon');
5
5
  const { initAssessFixture } = require('@contrast/test/fixtures');
6
6
  const mocks = require('@contrast/test/mocks');
7
7
 
8
-
9
8
  describe('assess makeSourceContext', function () {
10
9
  let core;
11
10
 
@@ -14,7 +13,7 @@ describe('assess makeSourceContext', function () {
14
13
  });
15
14
 
16
15
  it('will return `null` and log error when passed insufficient parameters', function() {
17
- const assessCtx = core.assess.makeSourceContext({}, {});
16
+ const assessCtx = core.assess.makeSourceContext({});
18
17
  expect(assessCtx).to.be.null;
19
18
  expect(core.logger.error).to.have.been.calledWithMatch({
20
19
  err: sinon.match.has('message'),
@@ -22,13 +21,14 @@ describe('assess makeSourceContext', function () {
22
21
  });
23
22
 
24
23
  it('construct the store correctly given req and res', function() {
25
- const req = mocks.incomingMessage();
26
- const res = {};
27
-
28
- const assessCtx = core.assess.makeSourceContext(req, res);
29
-
24
+ const mockSourceData = {
25
+ store: {},
26
+ incomingMessage: mocks.incomingMessage()
27
+ };
28
+ const assessCtx = core.assess.makeSourceContext(mockSourceData);
30
29
  expect(assessCtx).to.deep.include({
31
30
  propagationEventsCount: 0,
31
+ sampleInfo: null,
32
32
  sourceEventsCount: 0,
33
33
  reqData: {
34
34
  contentType: 'text/html',
@@ -16,10 +16,12 @@
16
16
  'use strict';
17
17
 
18
18
  const {
19
- StringPrototypeToLowerCase,
20
- JSONStringify,
21
- StringPrototypeSubstring,
22
- ResponseScanningRule
19
+ primordials: {
20
+ StringPrototypeToLowerCase,
21
+ JSONStringify,
22
+ StringPrototypeSubstring,
23
+ },
24
+ ResponseScanningRule,
23
25
  } = require('@contrast/common');
24
26
  const {
25
27
  escapeHtml,
@@ -201,7 +203,7 @@ module.exports = function(core) {
201
203
 
202
204
  reportFindings(sourceContext, {
203
205
  ruleId: ResponseScanningRule.CSP_HEADER_INSECURE,
204
- vulnerabilityMetadata: { data: JSON.stringify(vulnerabilityMetadata) }
206
+ vulnerabilityMetadata: { data: JSONStringify(vulnerabilityMetadata) }
205
207
  });
206
208
  }
207
209
  };
@@ -16,12 +16,15 @@
16
16
  'use strict';
17
17
 
18
18
  const {
19
- ArrayPrototypeJoin,
20
- StringPrototypeSubstring,
21
- StringPrototypeToLowerCase,
22
- StringPrototypeSplit,
23
- StringPrototypeTrim,
24
- StringPrototypeReplace
19
+ primordials: {
20
+ ArrayPrototypeJoin,
21
+ StringPrototypeSubstring,
22
+ StringPrototypeToLowerCase,
23
+ StringPrototypeSplit,
24
+ StringPrototypeTrim,
25
+ StringPrototypeReplace,
26
+ RegExpPrototypeTest
27
+ }
25
28
  } = require('@contrast/common');
26
29
 
27
30
  //
@@ -38,7 +41,7 @@ const reUnescapedHtml = /[&<>"']/g;
38
41
  const reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
39
42
 
40
43
  function escapeHtml(string) {
41
- return (string && reHasUnescapedHtml.test(string))
44
+ return (string && RegExpPrototypeTest.call(reHasUnescapedHtml, string))
42
45
  ? StringPrototypeReplace.call(string, reUnescapedHtml, (chr) => htmlEscapes[chr])
43
46
  : (string || '');
44
47
  }
@@ -257,7 +260,7 @@ function isSrcSecure(sources, defaultSources) {
257
260
  function isSourceSecure(sources) {
258
261
  return (
259
262
  sources.length > 0 &&
260
- sources.every((source) => !!source && !/\*/.test(source))
263
+ sources.every((source) => !!source && !RegExpPrototypeTest.call(/\*/, source))
261
264
  );
262
265
  }
263
266
 
@@ -15,7 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { StringPrototypeSplit, StringPrototypeSubstring, StringPrototypeToLowerCase, StringPrototypeTrim } = require('@contrast/common');
18
+ const { primordials: { StringPrototypeSplit, StringPrototypeSubstring, StringPrototypeToLowerCase, StringPrototypeTrim } } = require('@contrast/common');
19
19
 
20
20
  /**
21
21
  * @param {{