@contrast/assess 1.28.1 → 1.29.1

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 (58) hide show
  1. package/lib/crypto-analysis/install/crypto.js +2 -2
  2. package/lib/dataflow/propagation/install/JSON/parse-fn.js +5 -5
  3. package/lib/dataflow/propagation/install/JSON/parse.js +1 -1
  4. package/lib/dataflow/propagation/install/JSON/stringify.js +17 -9
  5. package/lib/dataflow/propagation/install/array-prototype-join.js +7 -6
  6. package/lib/dataflow/propagation/install/buffer.js +60 -2
  7. package/lib/dataflow/propagation/install/ejs/template.js +4 -4
  8. package/lib/dataflow/propagation/install/joi/boolean.js +3 -1
  9. package/lib/dataflow/propagation/install/joi/expression.js +3 -1
  10. package/lib/dataflow/propagation/install/joi/keys.js +5 -4
  11. package/lib/dataflow/propagation/install/joi/number.js +3 -1
  12. package/lib/dataflow/propagation/install/joi/string-schema.js +1 -5
  13. package/lib/dataflow/propagation/install/joi/utils.js +9 -5
  14. package/lib/dataflow/propagation/install/joi/values.js +3 -6
  15. package/lib/dataflow/propagation/install/mongoose/schema-map.js +2 -2
  16. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +2 -2
  17. package/lib/dataflow/propagation/install/mongoose/schema-string.js +2 -2
  18. package/lib/dataflow/propagation/install/path/basename.js +2 -2
  19. package/lib/dataflow/propagation/install/path/common.js +5 -5
  20. package/lib/dataflow/propagation/install/path/format.js +2 -2
  21. package/lib/dataflow/propagation/install/path/join-and-resolve.js +2 -2
  22. package/lib/dataflow/propagation/install/pug/index.js +1 -1
  23. package/lib/dataflow/propagation/install/querystring/parse.js +4 -3
  24. package/lib/dataflow/propagation/install/send.js +2 -2
  25. package/lib/dataflow/propagation/install/string/concat.js +3 -3
  26. package/lib/dataflow/propagation/install/string/index.js +3 -2
  27. package/lib/dataflow/propagation/install/string/match-all.js +0 -1
  28. package/lib/dataflow/propagation/install/string/match.js +2 -2
  29. package/lib/dataflow/propagation/install/string/replace.js +6 -6
  30. package/lib/dataflow/propagation/install/string/slice.js +2 -2
  31. package/lib/dataflow/propagation/install/string/split.js +2 -2
  32. package/lib/dataflow/propagation/install/string/substring.js +2 -2
  33. package/lib/dataflow/propagation/install/url/searchParams.js +74 -15
  34. package/lib/dataflow/sinks/index.js +1 -0
  35. package/lib/dataflow/sinks/install/child-process.js +3 -3
  36. package/lib/dataflow/sinks/install/fs.js +2 -2
  37. package/lib/dataflow/sinks/install/function.js +2 -2
  38. package/lib/dataflow/sinks/install/restify.js +208 -0
  39. package/lib/dataflow/sinks/install/vm.js +4 -4
  40. package/lib/dataflow/sources/handler.js +2 -2
  41. package/lib/dataflow/sources/index.js +1 -0
  42. package/lib/dataflow/sources/install/http.js +4 -4
  43. package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.js +85 -0
  44. package/lib/dataflow/sources/install/restify/index.js +32 -0
  45. package/lib/dataflow/sources/install/restify/jsonBodyParser.js +109 -0
  46. package/lib/dataflow/sources/install/restify/router.js +77 -0
  47. package/lib/dataflow/tag-utils.js +4 -4
  48. package/lib/dataflow/tracker.js +1 -0
  49. package/lib/event-factory.js +3 -3
  50. package/lib/get-policy.js +2 -2
  51. package/lib/index.d.ts +18 -0
  52. package/lib/make-source-context.js +2 -2
  53. package/lib/response-scanning/handlers/index.js +34 -34
  54. package/lib/response-scanning/handlers/utils.js +19 -12
  55. package/lib/response-scanning/install/http.js +34 -98
  56. package/lib/session-configuration/install/express-session.js +2 -2
  57. package/lib/session-configuration/install/fastify-cookie.js +2 -2
  58. package/package.json +4 -4
@@ -15,7 +15,14 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { join, substring, toLowerCase, split, trim, replace } = require('@contrast/common');
18
+ const {
19
+ ArrayPrototypeJoin,
20
+ StringPrototypeSubstring,
21
+ StringPrototypeToLowerCase,
22
+ StringPrototypeSplit,
23
+ StringPrototypeTrim,
24
+ StringPrototypeReplace
25
+ } = require('@contrast/common');
19
26
 
20
27
  //
21
28
  // General HTML utils
@@ -32,7 +39,7 @@ const reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
32
39
 
33
40
  function escapeHtml(string) {
34
41
  return (string && reHasUnescapedHtml.test(string))
35
- ? replace(string, reUnescapedHtml, (chr) => htmlEscapes[chr])
42
+ ? StringPrototypeReplace.call(string, reUnescapedHtml, (chr) => htmlEscapes[chr])
36
43
  : (string || '');
37
44
  }
38
45
 
@@ -54,7 +61,7 @@ function getElements(htmlTag, content = '') {
54
61
  [htmlTagRangeStart, htmlTagRangeLength] = getHtmlTagRange(htmlTag, content, offset);
55
62
  if (htmlTagRangeStart >= 0) {
56
63
  offset = htmlTagRangeStart + htmlTagRangeLength;
57
- elements.push(substring(content, htmlTagRangeStart, offset));
64
+ elements.push(StringPrototypeSubstring.call(content, htmlTagRangeStart, offset));
58
65
  }
59
66
  } while (htmlTagRangeStart >= 0);
60
67
 
@@ -69,7 +76,7 @@ function isHtmlContent(responseHeaders) {
69
76
  }
70
77
 
71
78
  // we may want to do a case-insensitive search through object keys here
72
- const contentType = toLowerCase(responseHeaders['Content-Type'] || responseHeaders['content-type'] || '');
79
+ const contentType = StringPrototypeToLowerCase.call(responseHeaders['Content-Type'] || responseHeaders['content-type'] || '');
73
80
 
74
81
  return (
75
82
  !contentType ||
@@ -88,7 +95,7 @@ function isParseableResponse(responseHeaders) {
88
95
  // we may want to do a case-insensitive search through object keys here
89
96
  let contentType =
90
97
  responseHeaders['Content-Type'] || responseHeaders['content-type'];
91
- if (contentType) contentType = toLowerCase(contentType);
98
+ if (contentType) contentType = StringPrototypeToLowerCase.call(contentType);
92
99
 
93
100
  return (
94
101
  !contentType ||
@@ -123,7 +130,7 @@ function getAttribute(attribute, htmlTag = '') {
123
130
  }
124
131
  }
125
132
 
126
- return substring(htmlTag, attrStart, attrEnd);
133
+ return StringPrototypeSubstring.call(htmlTag, attrStart, attrEnd);
127
134
  }
128
135
 
129
136
  /**
@@ -213,16 +220,16 @@ function checkCacheControlValue(value = '') {
213
220
  let noStore = false;
214
221
 
215
222
  value.forEach((directive) => {
216
- if (toLowerCase(directive).includes('no-cache')) {
223
+ if (StringPrototypeToLowerCase.call(directive).includes('no-cache')) {
217
224
  noCache = true;
218
225
  }
219
- if (toLowerCase(directive).includes('no-store')) {
226
+ if (StringPrototypeToLowerCase.call(directive).includes('no-store')) {
220
227
  noStore = true;
221
228
  }
222
229
  });
223
230
  return [noCache, noStore];
224
231
  } else {
225
- value = toLowerCase(value);
232
+ value = StringPrototypeToLowerCase.call(value);
226
233
  return [value.includes('no-cache'), value.includes('no-store')];
227
234
  }
228
235
  }
@@ -317,7 +324,7 @@ function formatSource({
317
324
  }
318
325
 
319
326
  data[`${key}Secure`] = isSecure;
320
- data[`${key}Value`] = join(sources, ' ');
327
+ data[`${key}Value`] = ArrayPrototypeJoin.call(sources, ' ');
321
328
  }
322
329
 
323
330
  /**
@@ -325,8 +332,8 @@ function formatSource({
325
332
  */
326
333
  function policyParser(policy) {
327
334
  const result = {};
328
- for (const directive of split(policy, ';')) {
329
- const [directiveKey, ...directiveValue] = split(trim(directive), /\s+/g);
335
+ for (const directive of StringPrototypeSplit.call(policy, ';')) {
336
+ const [directiveKey, ...directiveValue] = StringPrototypeSplit.call(StringPrototypeTrim.call(directive), /\s+/g);
330
337
  if (
331
338
  directiveKey &&
332
339
  !Object.prototype.hasOwnProperty.call(result, directiveKey)
@@ -15,7 +15,7 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const { split, substring, toLowerCase, trim } = require('@contrast/common');
18
+ const { StringPrototypeSplit, StringPrototypeSubstring, StringPrototypeToLowerCase, StringPrototypeTrim } = require('@contrast/common');
19
19
 
20
20
  /**
21
21
  * @param {{
@@ -46,13 +46,13 @@ module.exports = function(core) {
46
46
 
47
47
  function parseHeaders(rawHeaders) {
48
48
  const headersToParse = rawHeaders || '';
49
- const headersArray = split(headersToParse, '\r\n').filter(Boolean);
49
+ const headersArray = StringPrototypeSplit.call(headersToParse, '\r\n').filter(Boolean);
50
50
  return headersArray.reduce((acc, header) => {
51
51
  const idx = header.indexOf(':');
52
52
 
53
53
  if (idx > -1) {
54
- const name = toLowerCase(substring(header, 0, idx));
55
- const value = trim(substring(header, idx + 1));
54
+ const name = StringPrototypeToLowerCase.call(StringPrototypeSubstring.call(header, 0, idx));
55
+ const value = StringPrototypeTrim.call(StringPrototypeSubstring.call(header, idx + 1));
56
56
  const currentValue = acc[name];
57
57
  acc[name] = currentValue
58
58
  ? Array.isArray(currentValue)
@@ -67,25 +67,25 @@ module.exports = function(core) {
67
67
 
68
68
  const patchType = 'response-scanning';
69
69
 
70
- function writeHookChecks(sourceContext, evaluationContext) {
70
+ function writeHookChecks(sourceContext, resHeaders, resBody) {
71
71
  // Check only the rules concerning the response body
72
- handleAutoCompleteMissing(sourceContext, evaluationContext);
73
- handleCacheControlsMissing(sourceContext, evaluationContext);
74
- handleParameterPollution(sourceContext, evaluationContext);
75
- handleXxsProtectionHeaderDisabled(sourceContext, evaluationContext);
72
+ handleAutoCompleteMissing(sourceContext, resHeaders, resBody);
73
+ handleCacheControlsMissing(sourceContext, resHeaders, resBody);
74
+ handleParameterPollution(sourceContext, resBody);
75
+ handleXxsProtectionHeaderDisabled(sourceContext, resHeaders, resBody);
76
76
  }
77
77
 
78
- function endHookChecks(sourceContext, evaluationContext) {
78
+ function endHookChecks(sourceContext, resHeaders, resBody) {
79
79
  // Check all the response scanning rules
80
- handleAutoCompleteMissing(sourceContext, evaluationContext);
81
- handleCacheControlsMissing(sourceContext, evaluationContext);
82
- handleClickJackingControlsMissing(sourceContext, evaluationContext);
83
- handleParameterPollution(sourceContext, evaluationContext);
84
- handleCspHeader(sourceContext, evaluationContext);
85
- handleHstsHeaderMissing(sourceContext, evaluationContext);
86
- handleXPoweredByHeader(sourceContext, evaluationContext);
87
- handleXContentTypeHeaderMissing(sourceContext, evaluationContext);
88
- handleXxsProtectionHeaderDisabled(sourceContext, evaluationContext);
80
+ handleAutoCompleteMissing(sourceContext, resHeaders, resBody);
81
+ handleCacheControlsMissing(sourceContext, resHeaders, resBody);
82
+ handleClickJackingControlsMissing(sourceContext, resHeaders, resBody);
83
+ handleParameterPollution(sourceContext, resBody);
84
+ handleCspHeader(sourceContext, resHeaders);
85
+ handleHstsHeaderMissing(sourceContext, resHeaders);
86
+ handleXPoweredByHeader(sourceContext, resHeaders);
87
+ handleXContentTypeHeaderMissing(sourceContext, resHeaders);
88
+ handleXxsProtectionHeaderDisabled(sourceContext, resHeaders);
89
89
  }
90
90
 
91
91
  http.install = function() {
@@ -98,13 +98,7 @@ module.exports = function(core) {
98
98
  post(data) {
99
99
  const sourceContext = getSourceContext();
100
100
  if (!sourceContext) return;
101
-
102
- const evaluationContext = {
103
- responseBody: toLowerCase(data.args[0] || ''),
104
- responseHeaders: parseHeaders(data.result._header),
105
- };
106
-
107
- writeHookChecks(sourceContext, evaluationContext);
101
+ writeHookChecks(sourceContext, parseHeaders(data.result._header), StringPrototypeToLowerCase.call(data.args[0] || ''));
108
102
  }
109
103
  });
110
104
  }
@@ -116,20 +110,14 @@ module.exports = function(core) {
116
110
  post(data) {
117
111
  const sourceContext = getSourceContext();
118
112
  if (!sourceContext) return;
119
-
120
- const evaluationContext = {
121
- responseBody: toLowerCase(data.args[0] || ''),
122
- responseHeaders: parseHeaders(data.result._header),
123
- };
124
-
125
- endHookChecks(sourceContext, evaluationContext);
113
+ endHookChecks(sourceContext, parseHeaders(data.result._header), StringPrototypeToLowerCase.call(data.args[0] || ''));
126
114
  }
127
115
  });
128
116
  }
129
117
  });
130
118
 
131
119
  depHooks.resolve({ name: 'http2' }, (http2) => {
132
- // Patching the response object
120
+ // todo: NODE-3467
133
121
  {
134
122
  const name = 'http2.Http2ServerResponse.prototype.write';
135
123
  patcher.patch(http2.Http2ServerResponse.prototype, 'write', {
@@ -138,14 +126,8 @@ module.exports = function(core) {
138
126
  post(data) {
139
127
  const sourceContext = getSourceContext();
140
128
  if (!sourceContext) return;
141
-
142
129
  const headersSymbol = Object.getOwnPropertySymbols(data.obj).find(symbol => symbol.toString().includes('headers'));
143
- const evaluationContext = {
144
- responseBody: toLowerCase(data.args[0] || ''),
145
- responseHeaders: data.obj[headersSymbol],
146
- };
147
-
148
- writeHookChecks(sourceContext, evaluationContext);
130
+ writeHookChecks(sourceContext, data.obj[headersSymbol], StringPrototypeToLowerCase.call(data.args[0] || ''));
149
131
  }
150
132
  });
151
133
  }
@@ -159,70 +141,24 @@ module.exports = function(core) {
159
141
  if (!sourceContext) return;
160
142
 
161
143
  const headersSymbol = Object.getOwnPropertySymbols(data.result).find(symbol => symbol.toString().includes('headers'));
162
- const evaluationContext = {
163
- responseBody: toLowerCase(data.args[0] || ''),
164
- responseHeaders: data.result[headersSymbol],
165
- };
166
-
167
- endHookChecks(sourceContext, evaluationContext);
144
+ endHookChecks(sourceContext, data.result[headersSymbol], StringPrototypeToLowerCase.call(data.args[0] || ''));
168
145
  }
169
146
  });
170
147
  }
148
+ });
171
149
 
172
- // patching the stream object
173
- ['createServer', 'createSecureServer'].forEach((server) => {
174
- patcher.patch(http2, server, {
175
- name: `http2.${server}`,
176
- patchType,
177
- post(data) {
178
- // The other option is once again to patch the `emit` method of the server
179
- // similar to how we patch it for creating sources, but take action only on
180
- // `stream` events. I chose the currentt approach because the hook will only
181
- // run on a `stream` event instead of checking and returning on each `request`
182
- // connect.
183
- data.result._events = patcher.patch(data.result._events, 'stream', {
184
- name: 'stream',
185
- patchType: 'stream-patch',
186
- pre(data) {
187
- patcher.patch(data.args[0], 'write', {
188
- name: 'Http2Stream.write',
189
- patchType,
190
- post(data) {
191
- const sourceContext = getSourceContext();
192
- if (!sourceContext) return;
193
-
194
- const evaluationContext = {
195
- responseBody: toLowerCase(data.args[0] || ''),
196
- responseHeaders: data.obj.sentHeaders,
197
- };
198
-
199
- writeHookChecks(sourceContext, evaluationContext);
200
- }
201
- });
202
-
203
- patcher.patch(data.args[0], 'end', {
204
- name: 'Http2Stream.end',
205
- patchType,
206
- post(data) {
207
- const sourceContext = getSourceContext();
208
- if (!sourceContext) return;
209
-
210
- const evaluationContext = {
211
- responseBody: toLowerCase(data.args[0] || ''),
212
- responseHeaders: data.obj.sentHeaders,
213
- };
214
-
215
- endHookChecks(sourceContext, evaluationContext);
216
- }
217
- });
218
- }
219
- });
220
- }
221
- });
150
+ depHooks.resolve({ name: 'spdy', file: 'lib/spdy/response.js' }, (response) => {
151
+ patcher.patch(response, 'end', {
152
+ name: 'spdy.response.end',
153
+ patchType: 'test',
154
+ post(data) {
155
+ const sourceContext = getSourceContext();
156
+ if (!sourceContext) return;
157
+ endHookChecks(sourceContext, data.obj.getHeaders?.(), StringPrototypeToLowerCase.call(data.args[0] || ''));
158
+ }
222
159
  });
223
160
  });
224
161
  };
225
162
 
226
163
  return http;
227
164
  };
228
-
@@ -14,7 +14,7 @@
14
14
  */
15
15
  'use strict';
16
16
 
17
- const { toLowerCase } = require('@contrast/common');
17
+ const { StringPrototypeToLowerCase } = require('@contrast/common');
18
18
  const { patchType } = require('../common');
19
19
 
20
20
  /**
@@ -102,7 +102,7 @@ module.exports = function (core) {
102
102
  name: 'http.setHeader',
103
103
  patchType,
104
104
  pre({ args: [key, value] }) {
105
- if (toLowerCase(key) !== 'set-cookie') return;
105
+ if (StringPrototypeToLowerCase.call(key) !== 'set-cookie') return;
106
106
 
107
107
  if (checkForHTTPOnly) {
108
108
  handleHttpOnly(sourceContext, value, sessionEvent);
@@ -14,7 +14,7 @@
14
14
  */
15
15
  'use strict';
16
16
 
17
- const { toLowerCase } = require('@contrast/common');
17
+ const { StringPrototypeToLowerCase } = require('@contrast/common');
18
18
  const { patchType } = require('../common');
19
19
 
20
20
  /**
@@ -82,7 +82,7 @@ module.exports = function (core) {
82
82
  name: 'fastify.Reply.header',
83
83
  pre(data) {
84
84
  const [key, value] = data.args;
85
- if (toLowerCase(key) !== 'set-cookie') return;
85
+ if (StringPrototypeToLowerCase.call(key) !== 'set-cookie') return;
86
86
 
87
87
  const sourceContext = getSourceContext();
88
88
  if (!sourceContext) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/assess",
3
- "version": "1.28.1",
3
+ "version": "1.29.1",
4
4
  "description": "Contrast service providing framework-agnostic Assess support",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -11,14 +11,14 @@
11
11
  "types": "lib/index.d.ts",
12
12
  "engines": {
13
13
  "npm": ">=6.13.7 <7 || >= 8.3.1",
14
- "node": ">= 14.18.0"
14
+ "node": ">= 16.9.1"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "../scripts/test.sh"
18
18
  },
19
19
  "dependencies": {
20
- "@contrast/common": "1.21.0",
21
- "@contrast/distringuish": "^4.4.0",
20
+ "@contrast/common": "1.21.2",
21
+ "@contrast/distringuish": "^5.0.0",
22
22
  "@contrast/scopes": "1.4.1"
23
23
  }
24
24
  }