@contrast/assess 1.27.2 → 1.28.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/constants.js +3 -3
- package/lib/crypto-analysis/install/crypto.js +1 -1
- package/lib/dataflow/propagation/install/JSON/parse.js +2 -2
- package/lib/dataflow/propagation/install/JSON/stringify.js +10 -11
- package/lib/dataflow/propagation/install/array-prototype-join.js +10 -12
- package/lib/dataflow/propagation/install/buffer.js +3 -1
- package/lib/dataflow/propagation/install/contrast-methods/add.js +73 -72
- package/lib/dataflow/propagation/install/contrast-methods/number.js +4 -5
- package/lib/dataflow/propagation/install/contrast-methods/string.js +3 -2
- package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -6
- package/lib/dataflow/propagation/install/joi/boolean.js +2 -4
- package/lib/dataflow/propagation/install/joi/expression.js +2 -4
- package/lib/dataflow/propagation/install/joi/index.js +1 -1
- package/lib/dataflow/propagation/install/joi/number.js +2 -4
- package/lib/dataflow/propagation/install/joi/string-schema.js +8 -3
- package/lib/dataflow/propagation/install/joi/values.js +5 -1
- package/lib/dataflow/propagation/install/path/format.js +7 -4
- 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 +6 -7
- 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/string/concat.js +22 -23
- package/lib/dataflow/propagation/install/string/html-methods.js +6 -7
- package/lib/dataflow/propagation/install/string/index.js +3 -3
- package/lib/dataflow/propagation/install/string/match-all.js +10 -15
- package/lib/dataflow/propagation/install/string/match.js +5 -4
- package/lib/dataflow/propagation/install/string/replace.js +22 -16
- package/lib/dataflow/propagation/install/string/slice.js +7 -6
- package/lib/dataflow/propagation/install/string/split.js +17 -16
- package/lib/dataflow/propagation/install/string/substring.js +9 -8
- package/lib/dataflow/propagation/install/string/trim.js +4 -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/install/child-process.js +1 -1
- 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 +1 -1
- package/lib/dataflow/sinks/install/function.js +1 -1
- 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 +46 -9
- package/lib/dataflow/sinks/install/mysql.js +1 -1
- package/lib/dataflow/sinks/install/postgres.js +1 -3
- package/lib/dataflow/sinks/install/sequelize.js +1 -2
- package/lib/dataflow/sinks/install/vm.js +1 -1
- package/lib/dataflow/sources/install/body-parser1.js +12 -5
- package/lib/dataflow/sources/install/cookie-parser1.js +4 -3
- package/lib/dataflow/sources/install/qs6.js +7 -5
- package/lib/dataflow/sources/install/querystring.js +8 -2
- package/lib/dataflow/tag-utils.js +22 -5
- package/lib/get-source-context.js +2 -1
- package/lib/index.js +13 -0
- package/lib/session-configuration/install/express-session.js +1 -3
- package/lib/session-configuration/install/fastify-cookie.js +1 -1
- package/lib/session-configuration/install/hapi.js +1 -3
- package/lib/session-configuration/install/koa.js +1 -1
- package/package.json +2 -2
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { inspect } = require('@contrast/common');
|
|
19
18
|
const { patchType } = require('../../common');
|
|
20
19
|
|
|
21
20
|
module.exports = function(core) {
|
|
@@ -24,6 +23,7 @@ module.exports = function(core) {
|
|
|
24
23
|
patcher,
|
|
25
24
|
depHooks,
|
|
26
25
|
assess: {
|
|
26
|
+
inspect, // todo: remove
|
|
27
27
|
eventFactory: { createPropagationEvent },
|
|
28
28
|
dataflow: { tracker }
|
|
29
29
|
}
|
|
@@ -19,7 +19,6 @@ const {
|
|
|
19
19
|
join,
|
|
20
20
|
Rule: { CMD_INJECTION: ruleId },
|
|
21
21
|
isString,
|
|
22
|
-
inspect,
|
|
23
22
|
} = require('@contrast/common');
|
|
24
23
|
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
25
24
|
const { patchType } = require('../common');
|
|
@@ -35,6 +34,7 @@ module.exports = function(core) {
|
|
|
35
34
|
depHooks,
|
|
36
35
|
patcher,
|
|
37
36
|
assess: {
|
|
37
|
+
inspect, // todo: remove
|
|
38
38
|
getSourceContext,
|
|
39
39
|
eventFactory: { createSinkEvent },
|
|
40
40
|
dataflow: {
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const util = require('util');
|
|
19
18
|
const {
|
|
20
19
|
Rule: { REFLECTED_XSS: ruleId },
|
|
21
20
|
DataflowTag: {
|
|
@@ -52,14 +51,13 @@ module.exports = function(core) {
|
|
|
52
51
|
eventFactory: { createSinkEvent },
|
|
53
52
|
dataflow: {
|
|
54
53
|
tracker,
|
|
55
|
-
sinks: { isVulnerable, reportFindings, reportSafePositive }
|
|
54
|
+
sinks: { isVulnerable, reportFindings, reportSafePositive, isSafeContentType }
|
|
56
55
|
},
|
|
57
56
|
ruleScopes,
|
|
58
57
|
},
|
|
59
58
|
} = core;
|
|
60
59
|
|
|
61
60
|
const reflectedXss = core.assess.dataflow.sinks.express.reflectedXss = {};
|
|
62
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
63
61
|
|
|
64
62
|
const safeTags = [
|
|
65
63
|
`excluded:${ruleId}`,
|
|
@@ -81,7 +79,11 @@ module.exports = function(core) {
|
|
|
81
79
|
name,
|
|
82
80
|
patchType,
|
|
83
81
|
around: (next, data) => {
|
|
84
|
-
|
|
82
|
+
const sourceContext = getSourceContext(RULE, ruleId);
|
|
83
|
+
if (!sourceContext) return next();
|
|
84
|
+
|
|
85
|
+
const { contentType } = sourceContext.responseData;
|
|
86
|
+
if (contentType && isSafeContentType(contentType)) return next();
|
|
85
87
|
|
|
86
88
|
const [str] = data.args;
|
|
87
89
|
|
|
@@ -96,7 +98,7 @@ module.exports = function(core) {
|
|
|
96
98
|
tracked: true,
|
|
97
99
|
value: strInfo.value,
|
|
98
100
|
}],
|
|
99
|
-
context: `response.${method}(${
|
|
101
|
+
context: `response.${method}('${strInfo.value}')`,
|
|
100
102
|
history: [strInfo],
|
|
101
103
|
name,
|
|
102
104
|
moduleName: 'express',
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const util = require('util');
|
|
19
18
|
const {
|
|
20
19
|
Rule: { UNVALIDATED_REDIRECT: ruleId },
|
|
21
20
|
DataflowTag: {
|
|
@@ -46,6 +45,7 @@ module.exports = function(core) {
|
|
|
46
45
|
patcher,
|
|
47
46
|
config,
|
|
48
47
|
assess: {
|
|
48
|
+
inspect, // todo: remove
|
|
49
49
|
getSourceContext,
|
|
50
50
|
eventFactory: { createSinkEvent },
|
|
51
51
|
dataflow: {
|
|
@@ -56,7 +56,6 @@ module.exports = function(core) {
|
|
|
56
56
|
} = core;
|
|
57
57
|
|
|
58
58
|
const unvalidatedRedirect = core.assess.dataflow.sinks.express.unvalidatedRedirect = {};
|
|
59
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
60
59
|
|
|
61
60
|
const safeTags = [
|
|
62
61
|
`excluded:${ruleId}`,
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const util = require('util');
|
|
19
18
|
const {
|
|
20
19
|
Rule: { UNVALIDATED_REDIRECT: ruleId },
|
|
21
20
|
DataflowTag: {
|
|
@@ -65,6 +64,7 @@ module.exports = function(core) {
|
|
|
65
64
|
depHooks,
|
|
66
65
|
patcher,
|
|
67
66
|
assess: {
|
|
67
|
+
inspect, // todo: remove
|
|
68
68
|
getSourceContext,
|
|
69
69
|
eventFactory: { createSinkEvent },
|
|
70
70
|
dataflow: {
|
|
@@ -76,8 +76,6 @@ module.exports = function(core) {
|
|
|
76
76
|
const unvalidatedRedirect =
|
|
77
77
|
(core.assess.dataflow.sinks.fastify.unvalidatedRedirect = {});
|
|
78
78
|
|
|
79
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
80
|
-
|
|
81
79
|
const safeTags = [
|
|
82
80
|
`excluded:${ruleId}`,
|
|
83
81
|
CUSTOM_ENCODED,
|
|
@@ -25,7 +25,6 @@ const {
|
|
|
25
25
|
},
|
|
26
26
|
FS_METHODS,
|
|
27
27
|
Rule: { PATH_TRAVERSAL: ruleId },
|
|
28
|
-
inspect,
|
|
29
28
|
isString,
|
|
30
29
|
join,
|
|
31
30
|
} = require('@contrast/common');
|
|
@@ -36,6 +35,7 @@ module.exports = function(core) {
|
|
|
36
35
|
depHooks,
|
|
37
36
|
patcher,
|
|
38
37
|
assess: {
|
|
38
|
+
inspect, // todo: remove
|
|
39
39
|
getSourceContext,
|
|
40
40
|
eventFactory: { createSinkEvent },
|
|
41
41
|
dataflow: {
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
|
|
18
18
|
const {
|
|
19
19
|
isString,
|
|
20
|
-
inspect,
|
|
21
20
|
join,
|
|
22
21
|
DataflowTag: {
|
|
23
22
|
UNTRUSTED,
|
|
@@ -47,6 +46,7 @@ module.exports = function (core) {
|
|
|
47
46
|
logger,
|
|
48
47
|
patcher,
|
|
49
48
|
assess: {
|
|
49
|
+
inspect, // todo: remove
|
|
50
50
|
getSourceContext,
|
|
51
51
|
eventFactory: { createSinkEvent },
|
|
52
52
|
dataflow: {
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const util = require('util');
|
|
19
18
|
const {
|
|
20
19
|
Rule: { UNVALIDATED_REDIRECT: ruleId },
|
|
21
20
|
DataflowTag: {
|
|
@@ -46,6 +45,7 @@ module.exports = function(core) {
|
|
|
46
45
|
patcher,
|
|
47
46
|
config,
|
|
48
47
|
assess: {
|
|
48
|
+
inspect, // todo: remove
|
|
49
49
|
getSourceContext,
|
|
50
50
|
eventFactory: { createSinkEvent },
|
|
51
51
|
dataflow: {
|
|
@@ -56,7 +56,6 @@ module.exports = function(core) {
|
|
|
56
56
|
} = core;
|
|
57
57
|
|
|
58
58
|
const unvalidatedRedirect = core.assess.dataflow.sinks.hapi.unvalidatedRedirect = {};
|
|
59
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
60
59
|
|
|
61
60
|
const safeTags = [
|
|
62
61
|
`excluded:${ruleId}`,
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
|
|
18
18
|
const Url = require('url');
|
|
19
19
|
const {
|
|
20
|
-
inspect,
|
|
21
20
|
isString,
|
|
22
21
|
DataflowTag: {
|
|
23
22
|
UNTRUSTED,
|
|
@@ -46,6 +45,7 @@ module.exports = function(core) {
|
|
|
46
45
|
depHooks,
|
|
47
46
|
patcher,
|
|
48
47
|
assess: {
|
|
48
|
+
inspect, // todo: remove
|
|
49
49
|
getSourceContext,
|
|
50
50
|
eventFactory: { createSinkEvent },
|
|
51
51
|
dataflow: {
|
|
@@ -104,11 +104,12 @@ module.exports = function(core) {
|
|
|
104
104
|
pre(data) {
|
|
105
105
|
if (!getSourceContext(RULE, ruleId)) return;
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
// url <string> |<URL>
|
|
108
|
+
const [urlArg] = data.args;
|
|
109
|
+
if (!urlArg) return;
|
|
109
110
|
|
|
110
111
|
['host', 'hostname', 'localAddress', 'protocol'].forEach((key) => {
|
|
111
|
-
const value = getValueFromReq(
|
|
112
|
+
const value = getValueFromReq(urlArg, key);
|
|
112
113
|
if (!value) return;
|
|
113
114
|
|
|
114
115
|
const strInfo = tracker.getData(value);
|
|
@@ -116,7 +117,7 @@ module.exports = function(core) {
|
|
|
116
117
|
|
|
117
118
|
if (containsTrustedLib(strInfo.stack)) return;
|
|
118
119
|
|
|
119
|
-
const arg0 = isString(
|
|
120
|
+
const arg0 = isString(urlArg) ? urlArg : inspect(urlArg);
|
|
120
121
|
const idx = arg0.indexOf(value);
|
|
121
122
|
const urlTags = createAppendTags({}, strInfo.tags, idx);
|
|
122
123
|
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const util = require('util');
|
|
19
18
|
const {
|
|
20
19
|
DataflowTag: {
|
|
21
20
|
UNTRUSTED,
|
|
@@ -47,6 +46,7 @@ module.exports = function(core) {
|
|
|
47
46
|
patcher,
|
|
48
47
|
config,
|
|
49
48
|
assess: {
|
|
49
|
+
inspect, // todo: remove
|
|
50
50
|
getSourceContext,
|
|
51
51
|
eventFactory: { createSinkEvent },
|
|
52
52
|
dataflow: {
|
|
@@ -56,7 +56,7 @@ module.exports = function(core) {
|
|
|
56
56
|
},
|
|
57
57
|
} = core;
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
const safeTags = [
|
|
61
61
|
`excluded:${ruleId}`,
|
|
62
62
|
CUSTOM_ENCODED,
|
|
@@ -23,7 +23,6 @@ const {
|
|
|
23
23
|
ALPHANUM_SPACE_HYPHEN,
|
|
24
24
|
LIMITED_CHARS,
|
|
25
25
|
},
|
|
26
|
-
inspect
|
|
27
26
|
} = require('@contrast/common');
|
|
28
27
|
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
29
28
|
const { patchType } = require('../common');
|
|
@@ -48,6 +47,7 @@ module.exports = function(core) {
|
|
|
48
47
|
depHooks,
|
|
49
48
|
patcher,
|
|
50
49
|
assess: {
|
|
50
|
+
inspect, // todo: remove
|
|
51
51
|
getSourceContext,
|
|
52
52
|
eventFactory: { createSinkEvent },
|
|
53
53
|
dataflow: {
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const util = require('util');
|
|
18
17
|
const {
|
|
19
18
|
traverseValues,
|
|
20
19
|
Rule: { NOSQL_INJECTION_MONGO: ruleId },
|
|
@@ -51,6 +50,7 @@ module.exports = function (core) {
|
|
|
51
50
|
logger,
|
|
52
51
|
patcher,
|
|
53
52
|
assess: {
|
|
53
|
+
inspect, // todo: remove
|
|
54
54
|
getSourceContext,
|
|
55
55
|
eventFactory: { createSinkEvent },
|
|
56
56
|
dataflow: {
|
|
@@ -61,7 +61,6 @@ module.exports = function (core) {
|
|
|
61
61
|
} = core;
|
|
62
62
|
|
|
63
63
|
const instr = core.assess.dataflow.sinks.marsdb = {};
|
|
64
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
65
64
|
|
|
66
65
|
function getVulnerabilityInfo(query) {
|
|
67
66
|
let vulnInfo = null;
|
|
@@ -28,7 +28,6 @@ const {
|
|
|
28
28
|
isNonEmptyObject,
|
|
29
29
|
traverseValues,
|
|
30
30
|
isString,
|
|
31
|
-
inspect
|
|
32
31
|
} = require('@contrast/common');
|
|
33
32
|
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
34
33
|
const utils = require('../../tag-utils');
|
|
@@ -83,6 +82,7 @@ module.exports = function (core) {
|
|
|
83
82
|
logger,
|
|
84
83
|
patcher,
|
|
85
84
|
assess: {
|
|
85
|
+
inspect, // todo: remove
|
|
86
86
|
getSourceContext,
|
|
87
87
|
eventFactory: { createSinkEvent },
|
|
88
88
|
dataflow: {
|
|
@@ -236,6 +236,49 @@ module.exports = function (core) {
|
|
|
236
236
|
return { vulnInfo, reportSafe };
|
|
237
237
|
};
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Truncate `arg` values for reporting and get the adjusted `tags`.
|
|
241
|
+
* If an argument isn't directly relevant to the vulnerability, we report it by its type.
|
|
242
|
+
* If it is relevant, we truncate the value if it is an object or array, but not if it is a string.
|
|
243
|
+
* Ex1: Array truncations will look like `[...'<key>':'<value>'...]`
|
|
244
|
+
* Ex2: Object truncations will look like `{...'<key>':'<value>'...}`
|
|
245
|
+
* In the above examples, `key` will be the last value in the path array, and `value` should be the
|
|
246
|
+
* containing the injection. So whether the argument is a string or an object, the actual string value
|
|
247
|
+
* leading to the injection will not be truncated.
|
|
248
|
+
*/
|
|
249
|
+
function getAdjustedFields(origArgs, vulnInfo, vulnArgIdx) {
|
|
250
|
+
const { path, strInfo } = vulnInfo;
|
|
251
|
+
const coercedArgs = [];
|
|
252
|
+
let prefix = '';
|
|
253
|
+
let suffix = '';
|
|
254
|
+
|
|
255
|
+
if (path) {
|
|
256
|
+
let openChar, closeChar;
|
|
257
|
+
if (Array.isArray(origArgs[vulnArgIdx])) {
|
|
258
|
+
openChar = '[';
|
|
259
|
+
closeChar = ']';
|
|
260
|
+
} else {
|
|
261
|
+
openChar = '{';
|
|
262
|
+
closeChar = '}';
|
|
263
|
+
}
|
|
264
|
+
prefix = `${openChar}...'${path[path.length - 1]}':'`;
|
|
265
|
+
suffix = `'...${closeChar}`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < origArgs.length; i++) {
|
|
269
|
+
if (i === vulnArgIdx) {
|
|
270
|
+
coercedArgs.push({ value: `${prefix}${strInfo.value}${suffix}`, tracked: true });
|
|
271
|
+
} else {
|
|
272
|
+
coercedArgs.push({ value: origArgs[i]?.constructor?.name ?? typeof origArgs[i], tracked: false });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
args: coercedArgs,
|
|
278
|
+
tags: prefix ? utils.createAppendTags(null, strInfo.tags, prefix.length) : strInfo.tags,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
239
282
|
function createAroundHook(entity, name, method, getInfoMethod, vulnerableArgIdxs) {
|
|
240
283
|
const argsIdxsToCheck = vulnerableArgIdxs || [0];
|
|
241
284
|
return function (next, data) {
|
|
@@ -290,19 +333,13 @@ module.exports = function (core) {
|
|
|
290
333
|
: next();
|
|
291
334
|
}
|
|
292
335
|
|
|
293
|
-
const { path, strInfo } = vulnInfo;
|
|
294
336
|
const objName = getObjectName(obj, entity);
|
|
295
|
-
const args = origArgs
|
|
296
|
-
value: isString(arg) ? arg : inspect(arg, { depth: 4 }),
|
|
297
|
-
tracked: idx === vulnArgIdx,
|
|
298
|
-
}));
|
|
299
|
-
|
|
300
|
-
const tags = path ? utils.createAdjustedQueryTags(path, strInfo.tags, strInfo.value, args[vulnArgIdx].value) : strInfo?.tags;
|
|
337
|
+
const { args, tags } = getAdjustedFields(origArgs, vulnInfo, vulnArgIdx);
|
|
301
338
|
const resultVal = args[args.length - 1].value.startsWith('[Function') ? '' : 'Promise';
|
|
302
339
|
const sinkEvent = createSinkEvent({
|
|
303
340
|
args,
|
|
304
341
|
context: `${objName}.${method}(${args.map((a, idx) => isString(origArgs[idx]) ? `'${a.value}'` : a.value)})`,
|
|
305
|
-
history: [strInfo],
|
|
342
|
+
history: [vulnInfo.strInfo],
|
|
306
343
|
object: {
|
|
307
344
|
tracked: false,
|
|
308
345
|
value: `mongodb.${entity}`,
|
|
@@ -28,7 +28,6 @@ const {
|
|
|
28
28
|
UNTRUSTED
|
|
29
29
|
},
|
|
30
30
|
isString,
|
|
31
|
-
inspect,
|
|
32
31
|
} = require('@contrast/common');
|
|
33
32
|
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
34
33
|
|
|
@@ -54,6 +53,7 @@ module.exports = function(core) {
|
|
|
54
53
|
depHooks,
|
|
55
54
|
patcher,
|
|
56
55
|
assess: {
|
|
56
|
+
inspect, // todo: remove
|
|
57
57
|
getSourceContext,
|
|
58
58
|
eventFactory: { createSinkEvent },
|
|
59
59
|
dataflow: {
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const util = require('util');
|
|
19
18
|
const {
|
|
20
19
|
DataflowTag: {
|
|
21
20
|
CUSTOM_VALIDATED,
|
|
@@ -43,6 +42,7 @@ module.exports = function(core) {
|
|
|
43
42
|
depHooks,
|
|
44
43
|
patcher,
|
|
45
44
|
assess: {
|
|
45
|
+
inspect, // todo: remove
|
|
46
46
|
getSourceContext,
|
|
47
47
|
eventFactory: { createSinkEvent },
|
|
48
48
|
dataflow: {
|
|
@@ -60,8 +60,6 @@ module.exports = function(core) {
|
|
|
60
60
|
CUSTOM_ENCODED,
|
|
61
61
|
];
|
|
62
62
|
|
|
63
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
64
|
-
|
|
65
63
|
const postgres = core.assess.dataflow.sinks.postgres = {};
|
|
66
64
|
|
|
67
65
|
const preHook = (methodSignature) => (data) => {
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const util = require('util');
|
|
19
18
|
const {
|
|
20
19
|
Rule: { SQL_INJECTION: ruleId },
|
|
21
20
|
DataflowTag: {
|
|
@@ -42,6 +41,7 @@ module.exports = function (core) {
|
|
|
42
41
|
patcher,
|
|
43
42
|
config,
|
|
44
43
|
assess: {
|
|
44
|
+
inspect, // todo: remove
|
|
45
45
|
getSourceContext,
|
|
46
46
|
eventFactory: { createSinkEvent },
|
|
47
47
|
dataflow: {
|
|
@@ -59,7 +59,6 @@ module.exports = function (core) {
|
|
|
59
59
|
CUSTOM_ENCODED
|
|
60
60
|
];
|
|
61
61
|
const requiredTag = UNTRUSTED;
|
|
62
|
-
const inspect = patcher.unwrap(util.inspect);
|
|
63
62
|
|
|
64
63
|
const sequelize = (core.assess.dataflow.sinks.sequelize = {});
|
|
65
64
|
|
|
@@ -25,7 +25,6 @@ const {
|
|
|
25
25
|
LIMITED_CHARS,
|
|
26
26
|
},
|
|
27
27
|
Rule: { UNSAFE_CODE_EXECUTION: ruleId },
|
|
28
|
-
inspect,
|
|
29
28
|
isNonEmptyObject,
|
|
30
29
|
isString,
|
|
31
30
|
join,
|
|
@@ -56,6 +55,7 @@ module.exports = function (core) {
|
|
|
56
55
|
depHooks,
|
|
57
56
|
patcher,
|
|
58
57
|
assess: {
|
|
58
|
+
inspect, // todo: remove
|
|
59
59
|
getSourceContext,
|
|
60
60
|
eventFactory: { createSinkEvent },
|
|
61
61
|
dataflow: {
|
|
@@ -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
|
const METHODS = ['json', 'raw', 'text', 'urlencoded'];
|
|
@@ -27,12 +28,18 @@ const INPUT_TYPES = {
|
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
module.exports = function init(core) {
|
|
30
|
-
const {
|
|
31
|
+
const {
|
|
32
|
+
scopes,
|
|
33
|
+
assess: { dataflow, getSourceContext },
|
|
34
|
+
depHooks,
|
|
35
|
+
logger,
|
|
36
|
+
patcher,
|
|
37
|
+
} = core;
|
|
31
38
|
|
|
32
39
|
const preHook = (data) => {
|
|
33
40
|
const [req, , next] = data.args;
|
|
34
41
|
data.args[2] = scopes.wrap(function contrastNext(...args) {
|
|
35
|
-
const sourceContext =
|
|
42
|
+
const sourceContext = getSourceContext(SOURCE);
|
|
36
43
|
|
|
37
44
|
if (!sourceContext) {
|
|
38
45
|
logger.error({ funcKey: data.funcKey }, 'unable to handle source. Missing `sourceContext`');
|
|
@@ -68,7 +75,7 @@ module.exports = function init(core) {
|
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
try {
|
|
71
|
-
|
|
78
|
+
dataflow.sources.handle({
|
|
72
79
|
context,
|
|
73
80
|
data: _data,
|
|
74
81
|
name: data.name,
|
|
@@ -89,7 +96,7 @@ module.exports = function init(core) {
|
|
|
89
96
|
});
|
|
90
97
|
};
|
|
91
98
|
|
|
92
|
-
assess.dataflow.sources.bodyParser1Instrumentation = {
|
|
99
|
+
core.assess.dataflow.sources.bodyParser1Instrumentation = {
|
|
93
100
|
install() {
|
|
94
101
|
depHooks.resolve(
|
|
95
102
|
{ name: 'body-parser', version: '>=1.0.0' },
|
|
@@ -127,5 +134,5 @@ module.exports = function init(core) {
|
|
|
127
134
|
}
|
|
128
135
|
};
|
|
129
136
|
|
|
130
|
-
return assess.dataflow.sources.bodyParser1Instrumentation;
|
|
137
|
+
return core.assess.dataflow.sources.bodyParser1Instrumentation;
|
|
131
138
|
};
|
|
@@ -16,10 +16,11 @@
|
|
|
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 = function init(core) {
|
|
22
|
-
const { assess, depHooks, logger, patcher
|
|
23
|
+
const { assess, depHooks, logger, patcher } = core;
|
|
23
24
|
|
|
24
25
|
assess.dataflow.sources.cookieParser1Instrumentation = {
|
|
25
26
|
install() {
|
|
@@ -39,11 +40,11 @@ module.exports = function init(core) {
|
|
|
39
40
|
const { funcKey } = data;
|
|
40
41
|
const [req, , next] = data.args;
|
|
41
42
|
data.args[2] = function contrastNext(...args) {
|
|
42
|
-
const sourceContext =
|
|
43
|
+
const sourceContext = assess.getSourceContext(SOURCE);
|
|
43
44
|
|
|
44
45
|
if (!sourceContext) {
|
|
45
46
|
logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
|
|
46
|
-
return;
|
|
47
|
+
return next(...args);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
if (sourceContext.parsedCookies) {
|
|
@@ -15,17 +15,19 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { InputType } = require('@contrast/common');
|
|
18
|
+
const { InputType: { QUERYSTRING: inputType } } = require('@contrast/common');
|
|
19
19
|
const { patchType } = require('../common');
|
|
20
|
-
|
|
21
|
-
const inputType = InputType.QUERYSTRING;
|
|
20
|
+
const { InstrumentationType: { SOURCE } } = require('../../../constants');
|
|
22
21
|
|
|
23
22
|
module.exports = (core) => {
|
|
24
23
|
const {
|
|
25
24
|
depHooks,
|
|
26
25
|
patcher,
|
|
27
26
|
logger,
|
|
28
|
-
assess: {
|
|
27
|
+
assess: {
|
|
28
|
+
getSourceContext,
|
|
29
|
+
dataflow: { sources }
|
|
30
|
+
},
|
|
29
31
|
} = core;
|
|
30
32
|
|
|
31
33
|
// Patch `qs`
|
|
@@ -36,7 +38,7 @@ module.exports = (core) => {
|
|
|
36
38
|
name,
|
|
37
39
|
patchType,
|
|
38
40
|
post({ args, hooked, orig, result, funcKey }) {
|
|
39
|
-
const sourceContext =
|
|
41
|
+
const sourceContext = getSourceContext(SOURCE);
|
|
40
42
|
|
|
41
43
|
if (!sourceContext) {
|
|
42
44
|
logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
|
|
@@ -17,9 +17,15 @@
|
|
|
17
17
|
|
|
18
18
|
const { InputType } = require('@contrast/common');
|
|
19
19
|
const { patchType } = require('../common');
|
|
20
|
+
const { InstrumentationType: { SOURCE } } = require('../../../constants');
|
|
20
21
|
|
|
21
22
|
module.exports = (core) => {
|
|
22
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
assess: { getSourceContext },
|
|
25
|
+
depHooks,
|
|
26
|
+
patcher,
|
|
27
|
+
logger,
|
|
28
|
+
} = core;
|
|
23
29
|
|
|
24
30
|
core.assess.dataflow.sources.querystringInstrumentation = {
|
|
25
31
|
install() {
|
|
@@ -29,7 +35,7 @@ module.exports = (core) => {
|
|
|
29
35
|
name,
|
|
30
36
|
patchType,
|
|
31
37
|
post({ args, hooked, orig, result, funcKey }) {
|
|
32
|
-
const sourceContext =
|
|
38
|
+
const sourceContext = getSourceContext(SOURCE);
|
|
33
39
|
const inputType = InputType.QUERYSTRING;
|
|
34
40
|
|
|
35
41
|
if (!sourceContext) return;
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
+
const { split } = require('@contrast/common');
|
|
18
|
+
|
|
17
19
|
//
|
|
18
20
|
// This module implements tag range manipulation functions. There are generally
|
|
19
21
|
// two types of functions:
|
|
@@ -461,8 +463,7 @@ function createAdjustedQueryTags(path, tags, value, argString) {
|
|
|
461
463
|
break;
|
|
462
464
|
}
|
|
463
465
|
}
|
|
464
|
-
|
|
465
|
-
return idx >= 0 ? createAppendTags([], tags, idx) : [...tags];
|
|
466
|
+
return idx >= 0 ? createAppendTags([], tags, idx) : { ...tags };
|
|
466
467
|
}
|
|
467
468
|
|
|
468
469
|
/**
|
|
@@ -484,8 +485,8 @@ function createAdjustedQueryTags(path, tags, value, argString) {
|
|
|
484
485
|
* - "?test=str","%3Ftest%3Dstr",{"UNTRUSTED":[0,8]} => {"UNTRUSTED":[3,6,10,12]}
|
|
485
486
|
*/
|
|
486
487
|
function createEscapeTagRanges(input, result, tags) {
|
|
487
|
-
const inputArr =
|
|
488
|
-
const escapedArr =
|
|
488
|
+
const inputArr = split(input, '');
|
|
489
|
+
const escapedArr = split(result, '');
|
|
489
490
|
const overlap = inputArr.filter((x) => {
|
|
490
491
|
if (escapedArr.includes(x)) {
|
|
491
492
|
return x;
|
|
@@ -519,6 +520,21 @@ function createEscapeTagRanges(input, result, tags) {
|
|
|
519
520
|
return ret;
|
|
520
521
|
}
|
|
521
522
|
|
|
523
|
+
/**
|
|
524
|
+
* In reporting args, object, and return values, often the exact value isn't important.
|
|
525
|
+
* For untracked values that appear in call contexts it can be enough to just try to
|
|
526
|
+
* report the type of the arg/obj/result.
|
|
527
|
+
* Example: the call
|
|
528
|
+
* http.request('http://tracked-url', { method: 'post' });
|
|
529
|
+
* would have event context string limited to
|
|
530
|
+
* http.request('http://tracked-url,Object);
|
|
531
|
+
*
|
|
532
|
+
* @param {any} origValue value of event result, object, or arg
|
|
533
|
+
* @returns {string} the adjusted value for reporting
|
|
534
|
+
*/
|
|
535
|
+
function getAdjustedUntrackedValue(origValue) {
|
|
536
|
+
return origValue?.constructor?.name ?? (origValue === null ? 'null' : typeof arg);
|
|
537
|
+
}
|
|
522
538
|
|
|
523
539
|
module.exports = {
|
|
524
540
|
createSubsetTags,
|
|
@@ -528,5 +544,6 @@ module.exports = {
|
|
|
528
544
|
createTagsWithExclusion,
|
|
529
545
|
createAdjustedQueryTags,
|
|
530
546
|
createOverlappingTags,
|
|
531
|
-
createEscapeTagRanges
|
|
547
|
+
createEscapeTagRanges,
|
|
548
|
+
getAdjustedUntrackedValue,
|
|
532
549
|
};
|
|
@@ -39,7 +39,7 @@ module.exports = function(core) {
|
|
|
39
39
|
const ctx = sources.getStore()?.assess;
|
|
40
40
|
|
|
41
41
|
// policy will not exist if assess is altogether disabled for the active request e.g. url exclusion
|
|
42
|
-
if (!ctx
|
|
42
|
+
if (!ctx?.policy || instrumentation.isLocked()) return null;
|
|
43
43
|
|
|
44
44
|
switch (type) {
|
|
45
45
|
case InstrumentationType.PROPAGATOR: {
|
|
@@ -50,6 +50,7 @@ module.exports = function(core) {
|
|
|
50
50
|
if (ctx.sourceEventsCount > config.assess.max_context_source_events) return null;
|
|
51
51
|
break;
|
|
52
52
|
}
|
|
53
|
+
|
|
53
54
|
case InstrumentationType.RULE: {
|
|
54
55
|
const [ruleId] = rest;
|
|
55
56
|
if (!ruleId) break;
|