@contrast/assess 1.28.0 → 1.29.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.
- package/lib/crypto-analysis/install/crypto.js +3 -3
- package/lib/dataflow/propagation/install/JSON/parse-fn.js +5 -5
- package/lib/dataflow/propagation/install/JSON/parse.js +3 -3
- package/lib/dataflow/propagation/install/JSON/stringify.js +24 -17
- package/lib/dataflow/propagation/install/array-prototype-join.js +3 -3
- package/lib/dataflow/propagation/install/buffer.js +60 -2
- package/lib/dataflow/propagation/install/contrast-methods/add.js +1 -3
- package/lib/dataflow/propagation/install/ejs/template.js +3 -3
- package/lib/dataflow/propagation/install/joi/boolean.js +1 -1
- package/lib/dataflow/propagation/install/joi/expression.js +1 -1
- package/lib/dataflow/propagation/install/joi/index.js +1 -1
- package/lib/dataflow/propagation/install/joi/keys.js +5 -4
- package/lib/dataflow/propagation/install/joi/number.js +1 -1
- package/lib/dataflow/propagation/install/joi/string-schema.js +3 -2
- package/lib/dataflow/propagation/install/joi/utils.js +9 -5
- package/lib/dataflow/propagation/install/joi/values.js +4 -3
- package/lib/dataflow/propagation/install/mongoose/schema-map.js +2 -2
- package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +2 -2
- package/lib/dataflow/propagation/install/mongoose/schema-string.js +2 -2
- package/lib/dataflow/propagation/install/path/basename.js +2 -2
- package/lib/dataflow/propagation/install/path/common.js +5 -5
- package/lib/dataflow/propagation/install/path/format.js +7 -4
- package/lib/dataflow/propagation/install/path/join-and-resolve.js +2 -2
- package/lib/dataflow/propagation/install/path/parse.js +4 -5
- package/lib/dataflow/propagation/install/querystring/escape.js +1 -1
- package/lib/dataflow/propagation/install/querystring/parse.js +8 -8
- package/lib/dataflow/propagation/install/querystring/stringify.js +1 -1
- package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +2 -3
- package/lib/dataflow/propagation/install/send.js +2 -2
- package/lib/dataflow/propagation/install/string/concat.js +19 -19
- package/lib/dataflow/propagation/install/string/html-methods.js +1 -1
- package/lib/dataflow/propagation/install/string/index.js +4 -3
- package/lib/dataflow/propagation/install/string/match-all.js +3 -9
- package/lib/dataflow/propagation/install/string/match.js +6 -5
- package/lib/dataflow/propagation/install/string/replace.js +23 -17
- package/lib/dataflow/propagation/install/string/slice.js +5 -5
- package/lib/dataflow/propagation/install/string/split.js +13 -11
- package/lib/dataflow/propagation/install/string/substring.js +6 -5
- package/lib/dataflow/propagation/install/url/parse.js +1 -1
- package/lib/dataflow/propagation/install/url/searchParams.js +2 -1
- package/lib/dataflow/propagation/install/url/url.js +1 -1
- package/lib/dataflow/sinks/index.js +1 -0
- package/lib/dataflow/sinks/install/child-process.js +4 -4
- package/lib/dataflow/sinks/install/express/reflected-xss.js +7 -5
- package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +1 -2
- package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +1 -3
- package/lib/dataflow/sinks/install/fs.js +3 -3
- package/lib/dataflow/sinks/install/function.js +3 -3
- package/lib/dataflow/sinks/install/hapi/unvalidated-redirect.js +1 -2
- package/lib/dataflow/sinks/install/http/request.js +6 -5
- package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +2 -2
- package/lib/dataflow/sinks/install/libxmljs.js +1 -1
- package/lib/dataflow/sinks/install/marsdb.js +1 -2
- package/lib/dataflow/sinks/install/mongodb.js +1 -1
- package/lib/dataflow/sinks/install/mysql.js +1 -1
- package/lib/dataflow/sinks/install/postgres.js +1 -3
- package/lib/dataflow/sinks/install/restify.js +208 -0
- package/lib/dataflow/sinks/install/sequelize.js +1 -2
- package/lib/dataflow/sinks/install/vm.js +5 -5
- package/lib/dataflow/sources/handler.js +2 -2
- package/lib/dataflow/sources/index.js +1 -0
- package/lib/dataflow/sources/install/http.js +4 -4
- package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.js +85 -0
- package/lib/dataflow/sources/install/restify/index.js +32 -0
- package/lib/dataflow/sources/install/restify/jsonBodyParser.js +109 -0
- package/lib/dataflow/sources/install/restify/router.js +77 -0
- package/lib/dataflow/tag-utils.js +20 -4
- package/lib/dataflow/tracker.js +1 -0
- package/lib/event-factory.js +3 -3
- package/lib/get-policy.js +2 -2
- package/lib/index.d.ts +18 -0
- package/lib/index.js +13 -0
- package/lib/make-source-context.js +2 -2
- package/lib/response-scanning/handlers/index.js +10 -10
- package/lib/response-scanning/handlers/utils.js +19 -12
- package/lib/response-scanning/install/http.js +9 -59
- package/lib/session-configuration/install/express-session.js +3 -5
- package/lib/session-configuration/install/fastify-cookie.js +3 -3
- package/lib/session-configuration/install/hapi.js +1 -3
- package/lib/session-configuration/install/koa.js +1 -1
- package/package.json +4 -4
|
@@ -15,7 +15,14 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const {
|
|
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
|
-
?
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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 (
|
|
223
|
+
if (StringPrototypeToLowerCase.call(directive).includes('no-cache')) {
|
|
217
224
|
noCache = true;
|
|
218
225
|
}
|
|
219
|
-
if (
|
|
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 =
|
|
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`] =
|
|
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
|
|
329
|
-
const [directiveKey, ...directiveValue] =
|
|
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 {
|
|
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 =
|
|
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 =
|
|
55
|
-
const value =
|
|
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)
|
|
@@ -100,7 +100,7 @@ module.exports = function(core) {
|
|
|
100
100
|
if (!sourceContext) return;
|
|
101
101
|
|
|
102
102
|
const evaluationContext = {
|
|
103
|
-
responseBody:
|
|
103
|
+
responseBody: StringPrototypeToLowerCase.call(data.args[0] || ''),
|
|
104
104
|
responseHeaders: parseHeaders(data.result._header),
|
|
105
105
|
};
|
|
106
106
|
|
|
@@ -118,7 +118,7 @@ module.exports = function(core) {
|
|
|
118
118
|
if (!sourceContext) return;
|
|
119
119
|
|
|
120
120
|
const evaluationContext = {
|
|
121
|
-
responseBody:
|
|
121
|
+
responseBody: StringPrototypeToLowerCase.call(data.args[0] || ''),
|
|
122
122
|
responseHeaders: parseHeaders(data.result._header),
|
|
123
123
|
};
|
|
124
124
|
|
|
@@ -141,7 +141,7 @@ module.exports = function(core) {
|
|
|
141
141
|
|
|
142
142
|
const headersSymbol = Object.getOwnPropertySymbols(data.obj).find(symbol => symbol.toString().includes('headers'));
|
|
143
143
|
const evaluationContext = {
|
|
144
|
-
responseBody:
|
|
144
|
+
responseBody: StringPrototypeToLowerCase.call(data.args[0] || ''),
|
|
145
145
|
responseHeaders: data.obj[headersSymbol],
|
|
146
146
|
};
|
|
147
147
|
|
|
@@ -160,7 +160,7 @@ module.exports = function(core) {
|
|
|
160
160
|
|
|
161
161
|
const headersSymbol = Object.getOwnPropertySymbols(data.result).find(symbol => symbol.toString().includes('headers'));
|
|
162
162
|
const evaluationContext = {
|
|
163
|
-
responseBody:
|
|
163
|
+
responseBody: StringPrototypeToLowerCase.call(data.args[0] || ''),
|
|
164
164
|
responseHeaders: data.result[headersSymbol],
|
|
165
165
|
};
|
|
166
166
|
|
|
@@ -169,57 +169,7 @@ module.exports = function(core) {
|
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
171
|
|
|
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
|
-
});
|
|
222
|
-
});
|
|
172
|
+
// todo: patching the stream object (NODE-3467)
|
|
223
173
|
});
|
|
224
174
|
};
|
|
225
175
|
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
const { toLowerCase } = require('@contrast/common');
|
|
17
|
+
const { StringPrototypeToLowerCase } = require('@contrast/common');
|
|
19
18
|
const { patchType } = require('../common');
|
|
20
19
|
|
|
21
20
|
/**
|
|
@@ -27,6 +26,7 @@ const { patchType } = require('../common');
|
|
|
27
26
|
module.exports = function (core) {
|
|
28
27
|
const {
|
|
29
28
|
assess: {
|
|
29
|
+
inspect, // todo: remove
|
|
30
30
|
getSourceContext,
|
|
31
31
|
eventFactory: { createSessionEvent },
|
|
32
32
|
sessionConfiguration: {
|
|
@@ -40,8 +40,6 @@ module.exports = function (core) {
|
|
|
40
40
|
|
|
41
41
|
const expressSession = core.assess.sessionConfiguration.expressSession = {};
|
|
42
42
|
|
|
43
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
44
|
-
|
|
45
43
|
expressSession.install = function () {
|
|
46
44
|
return depHooks.resolve({ name: 'express-session' }, (session) => {
|
|
47
45
|
// Return the hooked function as the export.
|
|
@@ -104,7 +102,7 @@ module.exports = function (core) {
|
|
|
104
102
|
name: 'http.setHeader',
|
|
105
103
|
patchType,
|
|
106
104
|
pre({ args: [key, value] }) {
|
|
107
|
-
if (
|
|
105
|
+
if (StringPrototypeToLowerCase.call(key) !== 'set-cookie') return;
|
|
108
106
|
|
|
109
107
|
if (checkForHTTPOnly) {
|
|
110
108
|
handleHttpOnly(sourceContext, value, sessionEvent);
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const {
|
|
18
|
-
const { toLowerCase } = require('@contrast/common');
|
|
17
|
+
const { StringPrototypeToLowerCase } = require('@contrast/common');
|
|
19
18
|
const { patchType } = require('../common');
|
|
20
19
|
|
|
21
20
|
/**
|
|
@@ -27,6 +26,7 @@ const { patchType } = require('../common');
|
|
|
27
26
|
module.exports = function (core) {
|
|
28
27
|
const {
|
|
29
28
|
assess: {
|
|
29
|
+
inspect, // todo: remove
|
|
30
30
|
getSourceContext,
|
|
31
31
|
eventFactory: { createSessionEvent },
|
|
32
32
|
sessionConfiguration: {
|
|
@@ -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 (
|
|
85
|
+
if (StringPrototypeToLowerCase.call(key) !== 'set-cookie') return;
|
|
86
86
|
|
|
87
87
|
const sourceContext = getSourceContext();
|
|
88
88
|
if (!sourceContext) return;
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const util = require('util');
|
|
18
17
|
const { patchType } = require('../common');
|
|
19
18
|
|
|
20
19
|
module.exports = function (core) {
|
|
21
20
|
const {
|
|
22
21
|
assess: {
|
|
22
|
+
inspect, // todo: remove
|
|
23
23
|
eventFactory: { createSessionEvent },
|
|
24
24
|
sessionConfiguration: {
|
|
25
25
|
handleHttpOnly,
|
|
@@ -33,8 +33,6 @@ module.exports = function (core) {
|
|
|
33
33
|
|
|
34
34
|
const hapiSession = core.assess.sessionConfiguration.hapiSession = {};
|
|
35
35
|
|
|
36
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
37
|
-
|
|
38
36
|
hapiSession.install = function () {
|
|
39
37
|
return depHooks.resolve({ name: '@hapi/hapi', version: '>=18 <22' }, (hapi) => {
|
|
40
38
|
['server', 'Server'].forEach((server) => {
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const { inspect } = require('util');
|
|
18
17
|
const { patchType } = require('../common');
|
|
19
18
|
|
|
20
19
|
/**
|
|
@@ -26,6 +25,7 @@ const { patchType } = require('../common');
|
|
|
26
25
|
module.exports = function (core) {
|
|
27
26
|
const {
|
|
28
27
|
assess: {
|
|
28
|
+
inspect, // todo: remove
|
|
29
29
|
getSourceContext,
|
|
30
30
|
eventFactory: { createSessionEvent },
|
|
31
31
|
sessionConfiguration: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/assess",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0",
|
|
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
|
+
"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.
|
|
21
|
-
"@contrast/distringuish": "^
|
|
20
|
+
"@contrast/common": "1.21.1",
|
|
21
|
+
"@contrast/distringuish": "^5.0.0",
|
|
22
22
|
"@contrast/scopes": "1.4.1"
|
|
23
23
|
}
|
|
24
24
|
}
|