@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.
- package/lib/crypto-analysis/install/crypto.js +2 -2
- package/lib/dataflow/propagation/install/JSON/parse-fn.js +5 -5
- package/lib/dataflow/propagation/install/JSON/parse.js +1 -1
- package/lib/dataflow/propagation/install/JSON/stringify.js +17 -9
- package/lib/dataflow/propagation/install/array-prototype-join.js +7 -6
- package/lib/dataflow/propagation/install/buffer.js +60 -2
- package/lib/dataflow/propagation/install/ejs/template.js +4 -4
- package/lib/dataflow/propagation/install/joi/boolean.js +3 -1
- package/lib/dataflow/propagation/install/joi/expression.js +3 -1
- package/lib/dataflow/propagation/install/joi/keys.js +5 -4
- package/lib/dataflow/propagation/install/joi/number.js +3 -1
- package/lib/dataflow/propagation/install/joi/string-schema.js +1 -5
- package/lib/dataflow/propagation/install/joi/utils.js +9 -5
- package/lib/dataflow/propagation/install/joi/values.js +3 -6
- 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 +2 -2
- package/lib/dataflow/propagation/install/path/join-and-resolve.js +2 -2
- package/lib/dataflow/propagation/install/pug/index.js +1 -1
- package/lib/dataflow/propagation/install/querystring/parse.js +4 -3
- package/lib/dataflow/propagation/install/send.js +2 -2
- package/lib/dataflow/propagation/install/string/concat.js +3 -3
- package/lib/dataflow/propagation/install/string/index.js +3 -2
- package/lib/dataflow/propagation/install/string/match-all.js +0 -1
- package/lib/dataflow/propagation/install/string/match.js +2 -2
- package/lib/dataflow/propagation/install/string/replace.js +6 -6
- package/lib/dataflow/propagation/install/string/slice.js +2 -2
- package/lib/dataflow/propagation/install/string/split.js +2 -2
- package/lib/dataflow/propagation/install/string/substring.js +2 -2
- package/lib/dataflow/propagation/install/url/searchParams.js +74 -15
- package/lib/dataflow/sinks/index.js +1 -0
- package/lib/dataflow/sinks/install/child-process.js +3 -3
- package/lib/dataflow/sinks/install/fs.js +2 -2
- package/lib/dataflow/sinks/install/function.js +2 -2
- package/lib/dataflow/sinks/install/restify.js +208 -0
- package/lib/dataflow/sinks/install/vm.js +4 -4
- 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 +4 -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/make-source-context.js +2 -2
- package/lib/response-scanning/handlers/index.js +34 -34
- package/lib/response-scanning/handlers/utils.js +19 -12
- package/lib/response-scanning/install/http.js +34 -98
- package/lib/session-configuration/install/express-session.js +2 -2
- package/lib/session-configuration/install/fastify-cookie.js +2 -2
- 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)
|
|
@@ -67,25 +67,25 @@ module.exports = function(core) {
|
|
|
67
67
|
|
|
68
68
|
const patchType = 'response-scanning';
|
|
69
69
|
|
|
70
|
-
function writeHookChecks(sourceContext,
|
|
70
|
+
function writeHookChecks(sourceContext, resHeaders, resBody) {
|
|
71
71
|
// Check only the rules concerning the response body
|
|
72
|
-
handleAutoCompleteMissing(sourceContext,
|
|
73
|
-
handleCacheControlsMissing(sourceContext,
|
|
74
|
-
handleParameterPollution(sourceContext,
|
|
75
|
-
handleXxsProtectionHeaderDisabled(sourceContext,
|
|
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,
|
|
78
|
+
function endHookChecks(sourceContext, resHeaders, resBody) {
|
|
79
79
|
// Check all the response scanning rules
|
|
80
|
-
handleAutoCompleteMissing(sourceContext,
|
|
81
|
-
handleCacheControlsMissing(sourceContext,
|
|
82
|
-
handleClickJackingControlsMissing(sourceContext,
|
|
83
|
-
handleParameterPollution(sourceContext,
|
|
84
|
-
handleCspHeader(sourceContext,
|
|
85
|
-
handleHstsHeaderMissing(sourceContext,
|
|
86
|
-
handleXPoweredByHeader(sourceContext,
|
|
87
|
-
handleXContentTypeHeaderMissing(sourceContext,
|
|
88
|
-
handleXxsProtectionHeaderDisabled(sourceContext,
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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 {
|
|
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 (
|
|
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 {
|
|
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 (
|
|
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.
|
|
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
|
+
"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.2",
|
|
21
|
+
"@contrast/distringuish": "^5.0.0",
|
|
22
22
|
"@contrast/scopes": "1.4.1"
|
|
23
23
|
}
|
|
24
24
|
}
|