@contrast/assess 1.10.0 → 1.12.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 (119) hide show
  1. package/lib/dataflow/index.js +1 -2
  2. package/lib/dataflow/propagation/common.js +1 -1
  3. package/lib/dataflow/propagation/index.js +2 -1
  4. package/lib/dataflow/propagation/install/JSON/index.js +1 -1
  5. package/lib/dataflow/propagation/install/JSON/parse-fn.js +1 -1
  6. package/lib/dataflow/propagation/install/JSON/parse.js +3 -5
  7. package/lib/dataflow/propagation/install/JSON/stringify.js +3 -2
  8. package/lib/dataflow/propagation/install/array-prototype-join.js +3 -2
  9. package/lib/dataflow/propagation/install/buffer.js +3 -5
  10. package/lib/dataflow/propagation/install/contrast-methods/add.js +3 -2
  11. package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -1
  12. package/lib/dataflow/propagation/install/contrast-methods/number.js +1 -1
  13. package/lib/dataflow/propagation/install/contrast-methods/string.js +3 -5
  14. package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -5
  15. package/lib/dataflow/propagation/install/decode-uri-component.js +3 -2
  16. package/lib/dataflow/propagation/install/ejs/escape-xml.js +3 -2
  17. package/lib/dataflow/propagation/install/ejs/index.js +1 -1
  18. package/lib/dataflow/propagation/install/encode-uri-component.js +3 -2
  19. package/lib/dataflow/propagation/install/escape-html.js +3 -2
  20. package/lib/dataflow/propagation/install/escape.js +3 -2
  21. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +3 -2
  22. package/lib/dataflow/propagation/install/isnumeric-0.js +1 -1
  23. package/lib/dataflow/propagation/install/mongoose/common.js +20 -0
  24. package/lib/dataflow/propagation/install/mongoose/index.js +5 -9
  25. package/lib/dataflow/propagation/install/mongoose/schema-map.js +149 -0
  26. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +162 -0
  27. package/lib/dataflow/propagation/install/mongoose/schema-string.js +91 -39
  28. package/lib/dataflow/propagation/install/mysql-connection-escape.js +3 -2
  29. package/lib/dataflow/propagation/install/parse-int.js +1 -1
  30. package/lib/dataflow/propagation/install/path/basename.js +3 -5
  31. package/lib/dataflow/propagation/install/path/common.js +1 -1
  32. package/lib/dataflow/propagation/install/path/index.js +1 -1
  33. package/lib/dataflow/propagation/install/path/join-and-resolve.js +3 -5
  34. package/lib/dataflow/propagation/install/path/normalize.js +3 -5
  35. package/lib/dataflow/propagation/install/pug/index.js +1 -1
  36. package/lib/dataflow/propagation/install/pug-runtime-escape.js +3 -2
  37. package/lib/dataflow/propagation/install/querystring/index.js +1 -1
  38. package/lib/dataflow/propagation/install/querystring/parse.js +3 -2
  39. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +180 -0
  40. package/lib/dataflow/propagation/install/sequelize.js +3 -5
  41. package/lib/dataflow/propagation/install/sql-template-strings.js +3 -2
  42. package/lib/dataflow/propagation/install/string/concat.js +3 -2
  43. package/lib/dataflow/propagation/install/string/format-methods.js +3 -2
  44. package/lib/dataflow/propagation/install/string/html-methods.js +3 -2
  45. package/lib/dataflow/propagation/install/string/index.js +66 -1
  46. package/lib/dataflow/propagation/install/string/match-all.js +236 -0
  47. package/lib/dataflow/propagation/install/string/match.js +83 -37
  48. package/lib/dataflow/propagation/install/string/replace.js +4 -3
  49. package/lib/dataflow/propagation/install/string/slice.js +3 -2
  50. package/lib/dataflow/propagation/install/string/split.js +3 -2
  51. package/lib/dataflow/propagation/install/string/substring.js +3 -2
  52. package/lib/dataflow/propagation/install/string/trim.js +3 -2
  53. package/lib/dataflow/propagation/install/unescape.js +3 -2
  54. package/lib/dataflow/propagation/install/url/domain-parsers.js +3 -2
  55. package/lib/dataflow/propagation/install/url/index.js +3 -1
  56. package/lib/dataflow/propagation/install/url/parse.js +132 -0
  57. package/lib/dataflow/propagation/install/url/searchParams.js +140 -0
  58. package/lib/dataflow/propagation/install/url/url.js +11 -53
  59. package/lib/dataflow/propagation/install/validator/hooks.js +3 -2
  60. package/lib/dataflow/propagation/install/validator/index.js +1 -1
  61. package/lib/dataflow/propagation/install/validator/methods.js +1 -1
  62. package/lib/dataflow/sinks/common.js +1 -1
  63. package/lib/dataflow/sinks/index.js +1 -1
  64. package/lib/dataflow/sinks/install/child-process.js +2 -2
  65. package/lib/dataflow/sinks/install/eval.js +2 -2
  66. package/lib/dataflow/sinks/install/express/index.js +1 -1
  67. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +3 -3
  68. package/lib/dataflow/sinks/install/fastify/index.js +1 -1
  69. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +2 -2
  70. package/lib/dataflow/sinks/install/fs.js +2 -2
  71. package/lib/dataflow/sinks/install/function.js +2 -2
  72. package/lib/dataflow/sinks/install/http/index.js +1 -1
  73. package/lib/dataflow/sinks/install/http/request.js +2 -2
  74. package/lib/dataflow/sinks/install/http/server-response.js +2 -2
  75. package/lib/dataflow/sinks/install/koa/index.js +1 -1
  76. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +2 -2
  77. package/lib/dataflow/sinks/install/marsdb.js +2 -2
  78. package/lib/dataflow/sinks/install/mongodb.js +33 -26
  79. package/lib/dataflow/sinks/install/mssql.js +2 -2
  80. package/lib/dataflow/sinks/install/mysql.js +3 -3
  81. package/lib/dataflow/sinks/install/postgres.js +2 -2
  82. package/lib/dataflow/sinks/install/sequelize.js +2 -2
  83. package/lib/dataflow/sinks/install/sqlite3.js +2 -2
  84. package/lib/dataflow/sinks/install/vm.js +2 -2
  85. package/lib/dataflow/sources/common.js +1 -1
  86. package/lib/dataflow/sources/handler.js +3 -3
  87. package/lib/dataflow/sources/index.js +1 -1
  88. package/lib/dataflow/sources/install/body-parser1.js +1 -1
  89. package/lib/dataflow/sources/install/busboy1.js +1 -1
  90. package/lib/dataflow/sources/install/cookie-parser1.js +1 -1
  91. package/lib/dataflow/sources/install/express/index.js +1 -1
  92. package/lib/dataflow/sources/install/express/params.js +1 -1
  93. package/lib/dataflow/sources/install/express/parsedUrl.js +1 -1
  94. package/lib/dataflow/sources/install/fastify/fastify.js +1 -1
  95. package/lib/dataflow/sources/install/fastify/index.js +1 -1
  96. package/lib/dataflow/sources/install/formidable1.js +1 -1
  97. package/lib/dataflow/sources/install/http.js +2 -2
  98. package/lib/dataflow/sources/install/koa/index.js +1 -1
  99. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +1 -1
  100. package/lib/dataflow/sources/install/koa/koa-routers.js +1 -1
  101. package/lib/dataflow/sources/install/koa/koa2.js +1 -1
  102. package/lib/dataflow/sources/install/qs6.js +1 -1
  103. package/lib/dataflow/sources/install/querystring.js +1 -1
  104. package/lib/dataflow/tag-utils.js +1 -1
  105. package/lib/dataflow/tracker.js +2 -6
  106. package/lib/dataflow/utils/is-safe-content-type.js +1 -1
  107. package/lib/dataflow/utils/is-vulnerable.js +1 -1
  108. package/lib/{dataflow/event-factory.js → event-factory.js} +58 -2
  109. package/lib/index.js +4 -2
  110. package/lib/response-scanning/handlers/index.js +36 -30
  111. package/lib/response-scanning/handlers/utils.js +1 -1
  112. package/lib/response-scanning/index.js +1 -1
  113. package/lib/response-scanning/install/http.js +3 -3
  114. package/lib/session-configuration/common.js +19 -0
  115. package/lib/session-configuration/handlers.js +86 -0
  116. package/lib/session-configuration/index.js +6 -9
  117. package/lib/session-configuration/install/express-session.js +131 -0
  118. package/package.json +3 -3
  119. package/lib/session-configuration/install/http.js +0 -79
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -19,6 +19,7 @@ const { callChildComponentMethodsSync } = require('@contrast/common');
19
19
  const sessionConfiguration = require('./session-configuration');
20
20
  const dataflow = require('./dataflow');
21
21
  const responseScanning = require('./response-scanning');
22
+ const eventFactory = require('./event-factory');
22
23
 
23
24
  module.exports = function assess(core) {
24
25
  if (!core.config.assess.enable) return;
@@ -27,9 +28,10 @@ module.exports = function assess(core) {
27
28
 
28
29
  // Does this order matter? Probably not
29
30
  // 1. dataflow
30
- sessionConfiguration(core);
31
+ eventFactory(core);
31
32
  dataflow(core);
32
33
  responseScanning(core);
34
+ sessionConfiguration(core);
33
35
 
34
36
  // crypto
35
37
  // static (in coordination with rewriter)
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -15,6 +15,12 @@
15
15
 
16
16
  'use strict';
17
17
 
18
+ const {
19
+ toLowerCase,
20
+ stringify,
21
+ substring,
22
+ ResponseScanningRule
23
+ } = require('@contrast/common');
18
24
  const {
19
25
  escapeHtml,
20
26
  isHtmlContent,
@@ -26,7 +32,6 @@ const {
26
32
  getCspHeaders,
27
33
  checkCspSources
28
34
  } = require('./utils');
29
- const { toLowerCase, substring, ResponseScanningRule } = require('@contrast/common');
30
35
 
31
36
  module.exports = function(core) {
32
37
  const {
@@ -106,7 +111,7 @@ module.exports = function(core) {
106
111
  reportFindings(sourceContext, {
107
112
  ruleId: ResponseScanningRule.CACHE_CONTROLS_MISSING,
108
113
  vulnerabilityMetadata: {
109
- data: JSON.stringify(instructions)
114
+ data: stringify(instructions)
110
115
  }
111
116
  });
112
117
  }
@@ -176,7 +181,10 @@ module.exports = function(core) {
176
181
  delete vulnerabilityMetadata.referrerSecure;
177
182
  delete vulnerabilityMetadata.referrerValue;
178
183
 
179
- reportFindings(sourceContext, { ruleId: ResponseScanningRule.CSP_HEADER_INSECURE, vulnerabilityMetadata });
184
+ reportFindings(sourceContext, {
185
+ ruleId: ResponseScanningRule.CSP_HEADER_INSECURE,
186
+ vulnerabilityMetadata: { data: JSON.stringify(vulnerabilityMetadata) }
187
+ });
180
188
  }
181
189
  };
182
190
 
@@ -210,7 +218,27 @@ module.exports = function(core) {
210
218
  }
211
219
  };
212
220
 
213
- responseScanning.handlePoweredByHeader = function(sourceContext, { responseHeaders }) {
221
+ responseScanning.handleXContentTypeHeaderMissing = function(sourceContext, { responseHeaders }) {
222
+ const headerName = 'x-content-type-options';
223
+ let header = responseHeaders[headerName];
224
+
225
+ if (header) {
226
+ header = toLowerCase(header);
227
+ if (header === 'nosniff') {
228
+ return;
229
+ }
230
+ }
231
+
232
+ reportFindings(sourceContext, {
233
+ ruleId: ResponseScanningRule.XCONTENTTYPE_HEADER_MISSING,
234
+ vulnerabilityMetadata: {
235
+ data: header || ''
236
+ }
237
+ });
238
+ };
239
+
240
+ // NODE-3135
241
+ responseScanning.handleXPoweredByHeader = function(sourceContext, { responseHeaders }) {
214
242
  const headerName = 'x-powered-by';
215
243
  let header = responseHeaders[headerName];
216
244
 
@@ -226,7 +254,7 @@ module.exports = function(core) {
226
254
  ];
227
255
 
228
256
  reportFindings(sourceContext, {
229
- ruleId: ResponseScanningRule.POWERED_BY_HEADER,
257
+ ruleId: ResponseScanningRule.X_POWERED_BY_HEADER,
230
258
  vulnerabilityMetadata: {
231
259
  data: JSON.stringify(instructions)
232
260
  }
@@ -234,37 +262,15 @@ module.exports = function(core) {
234
262
  }
235
263
  };
236
264
 
237
- responseScanning.handleXContentTypeHeaderMissing = function(sourceContext, { responseHeaders }) {
238
- const headerName = 'x-content-type-options';
239
- let header = responseHeaders[headerName];
240
-
241
- if (header) {
242
- header = toLowerCase(header);
243
- if (header === 'nosniff') {
244
- return;
245
- }
246
- }
247
-
248
- reportFindings(sourceContext, {
249
- ruleId: ResponseScanningRule.XCONTENTTYPE_HEADER_MISSING,
250
- vulnerabilityMetadata: {
251
- data: header || ''
252
- }
253
- });
254
- };
255
-
256
265
  responseScanning.handleXxsProtectionHeaderDisabled = function(sourceContext, { responseHeaders }) {
257
266
  const header = responseHeaders['x-xss-protection'];
258
267
 
259
- // This header is set by default, so `header` should always be present.
260
- if (header && header.startsWith('1')) {
261
- return;
262
- }
268
+ if (header?.startsWith?.('1')) return;
263
269
 
264
270
  reportFindings(sourceContext, {
265
271
  ruleId: ResponseScanningRule.XXSPROTECTION_HEADER_DISABLED,
266
272
  vulnerabilityMetadata: {
267
- data: header
273
+ data: header,
268
274
  }
269
275
  });
270
276
  };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -30,7 +30,7 @@ module.exports = function(core) {
30
30
  handleParameterPollution,
31
31
  handleCspHeader,
32
32
  handleHstsHeaderMissing,
33
- handlePoweredByHeader,
33
+ handleXPoweredByHeader,
34
34
  handleXContentTypeHeaderMissing,
35
35
  handleXxsProtectionHeaderDisabled,
36
36
  }
@@ -76,7 +76,7 @@ module.exports = function(core) {
76
76
  handleParameterPollution(sourceContext, evaluationContext);
77
77
  handleCspHeader(sourceContext, evaluationContext);
78
78
  handleHstsHeaderMissing(sourceContext, evaluationContext);
79
- handlePoweredByHeader(sourceContext, evaluationContext);
79
+ handleXPoweredByHeader(sourceContext, evaluationContext);
80
80
  handleXContentTypeHeaderMissing(sourceContext, evaluationContext);
81
81
  handleXxsProtectionHeaderDisabled(sourceContext, evaluationContext);
82
82
  }
@@ -0,0 +1,19 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+ 'use strict';
16
+
17
+ module.exports = {
18
+ patchType: 'session-configuration'
19
+ };
@@ -0,0 +1,86 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const {
19
+ Event,
20
+ SessionConfigurationRule: { HTTPONLY, SECURE_FLAG_MISSING },
21
+ } = require('@contrast/common');
22
+
23
+ module.exports = function (core) {
24
+ const {
25
+ assess: { sessionConfiguration },
26
+ messages,
27
+ } = core;
28
+
29
+ const checkCookieValue = (ruleId, sinkEvent, cookieValue, sourceContext) => {
30
+ if (cookieValue.includes(ruleId === HTTPONLY ? 'httponly' : 'secure')) {
31
+ return;
32
+ }
33
+
34
+ sessionConfiguration.reportFindings(sourceContext, {
35
+ ruleId,
36
+ sinkEvent,
37
+ props: {
38
+ evidence: cookieValue,
39
+ },
40
+ });
41
+ };
42
+
43
+ const handleCookie = (
44
+ sourceContext,
45
+ cookieValue,
46
+ ruleId,
47
+ sessionEvent
48
+ ) => {
49
+ if (Array.isArray(cookieValue)) {
50
+ return cookieValue.forEach((value) =>
51
+ checkCookieValue(ruleId, sessionEvent, value, sourceContext)
52
+ );
53
+ }
54
+
55
+ checkCookieValue(ruleId, sessionEvent, cookieValue, sourceContext);
56
+ };
57
+
58
+ sessionConfiguration.handleHttpOnly = function (
59
+ sourceContext,
60
+ cookieValue,
61
+ sessionEvent
62
+ ) {
63
+ handleCookie(sourceContext, cookieValue, HTTPONLY, sessionEvent);
64
+ };
65
+
66
+ sessionConfiguration.handleSecure = function (
67
+ sourceContext,
68
+ cookieValue,
69
+ sessionEvent
70
+ ) {
71
+ handleCookie(sourceContext, cookieValue, SECURE_FLAG_MISSING, sessionEvent);
72
+ };
73
+
74
+ // _sourceContext is unused
75
+ sessionConfiguration.reportFindings = function (
76
+ _sourceContext,
77
+ vulnerabilityMetadata
78
+ ) {
79
+ messages.emit(
80
+ Event.ASSESS_SESSION_CONFIGURATION_FINDING,
81
+ vulnerabilityMetadata
82
+ );
83
+ };
84
+
85
+ return sessionConfiguration;
86
+ };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -15,16 +15,13 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { callChildComponentMethodsSync, Event } = require('@contrast/common');
18
+ const { callChildComponentMethodsSync } = require('@contrast/common');
19
19
 
20
20
  module.exports = function(core) {
21
- const { messages } = core;
22
- const sessionConfiguration = core.assess.sessionConfiguration = {
23
- reportFindings(_sourceContext, vulnerabilityMetadata) {
24
- messages.emit(Event.ASSESS_SESSION_CONFIGURATION_FINDING, vulnerabilityMetadata);
25
- },
26
- };
27
- require('./install/http')(core);
21
+ const sessionConfiguration = core.assess.sessionConfiguration = {};
22
+
23
+ require('./handlers')(core);
24
+ require('./install/express-session')(core);
28
25
 
29
26
  sessionConfiguration.install = function() {
30
27
  callChildComponentMethodsSync(sessionConfiguration, 'install');
@@ -0,0 +1,131 @@
1
+ /*
2
+ * Copyright: 2023 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+ 'use strict';
16
+
17
+ const util = require('util');
18
+ const { toLowerCase } = require('@contrast/common');
19
+ const { patchType } = require('../common');
20
+
21
+ module.exports = function (core) {
22
+ const {
23
+ assess: {
24
+ eventFactory: { createSessionEvent },
25
+ sessionConfiguration: {
26
+ handleHttpOnly,
27
+ handleSecure,
28
+ },
29
+ },
30
+ depHooks,
31
+ patcher,
32
+ scopes: { sources },
33
+ } = core;
34
+
35
+ const expressSession = core.assess.sessionConfiguration.expressSession = {};
36
+
37
+ const inspect = patcher.unwrap(util.inspect);
38
+
39
+ expressSession.install = function () {
40
+ return depHooks.resolve({ name: 'express-session' }, (session) => {
41
+ // Return the hooked function as the export.
42
+ const hooked = patcher.patch(session, {
43
+ name: 'express.hookedSessionConstructor',
44
+ patchType,
45
+ post(data) {
46
+ const options = data.args[0];
47
+
48
+ // obfuscate the cookie secret
49
+ if (Array.isArray(data.args) && data.args[0] && data.args[0].secret) {
50
+ data.args[0].secret = '[HIDDEN]';
51
+ }
52
+
53
+ const { cookie } = options || {};
54
+ const hasOwnPropertyHttpOnly = cookie && Object.prototype.hasOwnProperty.call(
55
+ cookie,
56
+ 'httpOnly'
57
+ );
58
+
59
+ // httpOnly is true by default if it's not provided
60
+ const checkForHTTPOnly =
61
+ cookie && hasOwnPropertyHttpOnly
62
+ ? !(cookie.httpOnly === true)
63
+ : false;
64
+
65
+ // secure is false by default
66
+ const checkForSecure = cookie ? !(cookie.secure === true) : true;
67
+
68
+ // skip instrumentation since the options are set correctly
69
+ if (!checkForHTTPOnly && !checkForSecure) return;
70
+
71
+ const sessionEvent = createSessionEvent({
72
+ args: [{
73
+ tracked: false,
74
+ value: inspect(options),
75
+ }],
76
+ context: `expressSession(${inspect(data.args)})`,
77
+ history: [],
78
+ name: 'express.hookedSessionConstructor',
79
+ moduleName: 'express-session',
80
+ methodName: '',
81
+ object: {
82
+ tracked: false,
83
+ value: 'Express.Response',
84
+ },
85
+ result: {
86
+ tracked: false,
87
+ value: undefined,
88
+ },
89
+ source: 'P0',
90
+ stacktraceOpts: {
91
+ constructorOpt: data.hooked,
92
+ },
93
+ framework: 'express',
94
+ options,
95
+ });
96
+
97
+ patcher.patch(data, 'result', {
98
+ name: 'express-session.middleware',
99
+ patchType,
100
+ pre(data) {
101
+ const [, res] = data.args;
102
+
103
+ const sourceContext = sources.getStore()?.assess;
104
+ if (!sourceContext) return;
105
+
106
+ patcher.patch(res, 'setHeader', {
107
+ name: 'http.setHeader',
108
+ patchType,
109
+ pre({ args: [key, value] }) {
110
+ if (toLowerCase(key) !== 'set-cookie') return;
111
+
112
+ if (checkForHTTPOnly) {
113
+ handleHttpOnly(sourceContext, value, sessionEvent);
114
+ }
115
+
116
+ if (checkForSecure) {
117
+ handleSecure(sourceContext, value, sessionEvent);
118
+ }
119
+ }
120
+ });
121
+ },
122
+ });
123
+ }
124
+ });
125
+
126
+ return hooked;
127
+ });
128
+ };
129
+
130
+ return expressSession;
131
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/assess",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -13,9 +13,9 @@
13
13
  "node": ">= 14.15.0"
14
14
  },
15
15
  "dependencies": {
16
- "@contrast/distringuish": "^4.1.0",
16
+ "@contrast/distringuish": "^4.4.0",
17
17
  "@contrast/scopes": "1.4.0",
18
- "@contrast/common": "1.13.0",
18
+ "@contrast/common": "1.14.0",
19
19
  "parseurl": "^1.3.3"
20
20
  }
21
21
  }
@@ -1,79 +0,0 @@
1
- /*
2
- * Copyright: 2022 Contrast Security, Inc
3
- * Contact: support@contrastsecurity.com
4
- * License: Commercial
5
-
6
- * NOTICE: This Software and the patented inventions embodied within may only be
7
- * used as part of Contrast Security’s commercial offerings. Even though it is
8
- * made available through public repositories, use of this Software is subject to
9
- * the applicable End User Licensing Agreement found at
10
- * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
- * between Contrast Security and the End User. The Software may not be reverse
12
- * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
- * way not consistent with the End User License Agreement.
14
- */
15
-
16
- 'use strict';
17
-
18
- const { SessionConfigurationRule, split, toLowerCase } = require('@contrast/common');
19
-
20
- module.exports = function(core) {
21
- const {
22
- depHooks,
23
- patcher,
24
- scopes: { sources },
25
- assess: {
26
- sessionConfiguration: {
27
- reportFindings
28
- }
29
- }
30
- } = core;
31
- const http = core.assess.sessionConfiguration.httpInstrumentation = {};
32
-
33
- const patchType = 'session-configuration';
34
-
35
- http.install = function() {
36
- [
37
- { name: 'http', responseObj: 'ServerResponse' },
38
- { name: 'https', responseObj: 'ServerResponse' },
39
- { name: 'http2', responseObj: 'Http2ServerResponse' }
40
- ].forEach(({ name, responseObj }) => {
41
- depHooks.resolve({ name }, (module) => {
42
- patcher.patch(module[responseObj].prototype, 'setHeader', {
43
- name: `${name}.${responseObj}.prototype.setHeader`,
44
- patchType,
45
- post(data) {
46
- const sourceContext = sources.getStore()?.assess;
47
- if (!sourceContext) return;
48
-
49
- const [key, val] = data.args;
50
- if (key === 'Set-Cookie') {
51
- const [cookies] = val;
52
- const parsedCookies = split(toLowerCase(cookies), '; ');
53
-
54
- if (!parsedCookies.includes('httponly')) {
55
- reportFindings(sourceContext, {
56
- ruleId: SessionConfigurationRule.HTTPONLY,
57
- props: {
58
- evidence: cookies
59
- }
60
- });
61
- }
62
-
63
- if (!parsedCookies.includes('secure')) {
64
- reportFindings(sourceContext, {
65
- ruleId: SessionConfigurationRule.SECURE_FLAG_MISSING,
66
- props: {
67
- evidence: cookies
68
- }
69
- });
70
- }
71
- }
72
- }
73
- });
74
- });
75
- });
76
- };
77
-
78
- return http;
79
- };