@contrast/assess 1.18.0 → 1.19.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/constants.js +26 -0
- package/lib/crypto-analysis/common.js +20 -0
- package/lib/crypto-analysis/index.js +44 -0
- package/lib/crypto-analysis/install/crypto.js +151 -0
- package/lib/crypto-analysis/install/math.js +99 -0
- package/lib/dataflow/propagation/install/JSON/parse.js +12 -11
- package/lib/dataflow/propagation/install/JSON/stringify.js +1 -1
- package/lib/dataflow/propagation/install/ejs/escape-xml.js +2 -2
- package/lib/dataflow/propagation/install/ejs/index.js +1 -0
- package/lib/dataflow/propagation/install/ejs/template.js +77 -0
- package/lib/dataflow/propagation/install/util-format.js +9 -3
- package/lib/dataflow/sinks/install/child-process.js +20 -14
- package/lib/dataflow/sinks/install/eval.js +16 -14
- package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +14 -8
- package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +12 -5
- package/lib/dataflow/sinks/install/fs.js +7 -7
- package/lib/dataflow/sinks/install/function.js +8 -12
- package/lib/dataflow/sinks/install/http/request.js +16 -8
- package/lib/dataflow/sinks/install/http/server-response.js +11 -2
- package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +15 -8
- package/lib/dataflow/sinks/install/libxmljs.js +15 -10
- package/lib/dataflow/sinks/install/marsdb.js +13 -8
- package/lib/dataflow/sinks/install/mongodb.js +25 -15
- package/lib/dataflow/sinks/install/mssql.js +20 -9
- package/lib/dataflow/sinks/install/mysql.js +15 -8
- package/lib/dataflow/sinks/install/node-serialize.js +15 -17
- package/lib/dataflow/sinks/install/postgres.js +17 -4
- package/lib/dataflow/sinks/install/sequelize.js +16 -9
- package/lib/dataflow/sinks/install/sqlite3.js +20 -7
- package/lib/dataflow/sinks/install/vm.js +19 -17
- package/lib/dataflow/sources/install/http.js +14 -42
- package/lib/dataflow/sources/install/koa/index.js +1 -0
- package/lib/dataflow/sources/install/koa/koa-multer.js +102 -0
- package/lib/dataflow/sources/install/multer1.js +25 -51
- package/lib/dataflow/sources/install/querystring.js +1 -4
- package/lib/event-factory.js +47 -0
- package/lib/get-policy.js +68 -0
- package/lib/get-source-context.js +62 -0
- package/lib/index.d.ts +50 -0
- package/lib/index.js +20 -19
- package/lib/make-source-context.js +74 -0
- package/lib/response-scanning/handlers/index.js +55 -28
- package/lib/response-scanning/install/http.js +13 -7
- package/lib/rule-scopes.js +48 -0
- package/lib/session-configuration/handlers.js +4 -3
- package/lib/session-configuration/install/express-session.js +8 -2
- package/package.json +2 -2
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
const util = require('util');
|
|
19
19
|
const {
|
|
20
|
+
Rule: { UNVALIDATED_REDIRECT: ruleId },
|
|
20
21
|
DataflowTag: {
|
|
21
22
|
UNTRUSTED,
|
|
22
23
|
CUSTOM_ENCODED,
|
|
@@ -27,18 +28,25 @@ const {
|
|
|
27
28
|
},
|
|
28
29
|
isString
|
|
29
30
|
} = require('@contrast/common');
|
|
30
|
-
const {
|
|
31
|
+
const { InstrumentationType: { RULE } } = require('../../../../constants');
|
|
31
32
|
const { createSubsetTags } = require('../../../tag-utils');
|
|
33
|
+
const { patchType, filterSafeTags } = require('../../common');
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @param {{
|
|
37
|
+
* assess: import('@contrast/assess').Assess,
|
|
38
|
+
* config: import('@contrast/config').Config,
|
|
39
|
+
* logger: import('@contrast/logger').Logger,
|
|
40
|
+
* }} core
|
|
41
|
+
* @returns {import('@contrast/common').Installable}
|
|
42
|
+
*/
|
|
35
43
|
module.exports = function(core) {
|
|
36
44
|
const {
|
|
37
45
|
depHooks,
|
|
38
46
|
patcher,
|
|
39
47
|
config,
|
|
40
|
-
scopes: { sources },
|
|
41
48
|
assess: {
|
|
49
|
+
getSourceContext,
|
|
42
50
|
eventFactory: { createSinkEvent },
|
|
43
51
|
dataflow: {
|
|
44
52
|
tracker,
|
|
@@ -46,9 +54,8 @@ module.exports = function(core) {
|
|
|
46
54
|
},
|
|
47
55
|
},
|
|
48
56
|
} = core;
|
|
49
|
-
const unvalidatedRedirect =
|
|
50
|
-
(core.assess.dataflow.sinks.express.unvalidatedRedirect = {});
|
|
51
57
|
|
|
58
|
+
const unvalidatedRedirect = core.assess.dataflow.sinks.express.unvalidatedRedirect = {};
|
|
52
59
|
const inspect = patcher.unwrap(util.inspect);
|
|
53
60
|
|
|
54
61
|
const safeTags = [
|
|
@@ -66,8 +73,7 @@ module.exports = function(core) {
|
|
|
66
73
|
name: 'Express.Response.location',
|
|
67
74
|
patchType,
|
|
68
75
|
pre: (data) => {
|
|
69
|
-
|
|
70
|
-
if (!assessStore) return;
|
|
76
|
+
if (!getSourceContext(RULE, ruleId)) return;
|
|
71
77
|
|
|
72
78
|
let [url] = data.args;
|
|
73
79
|
if (url === 'back') {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
const util = require('util');
|
|
19
19
|
const {
|
|
20
|
+
Rule: { UNVALIDATED_REDIRECT: ruleId },
|
|
20
21
|
DataflowTag: {
|
|
21
22
|
UNTRUSTED,
|
|
22
23
|
CUSTOM_ENCODED,
|
|
@@ -27,11 +28,10 @@ const {
|
|
|
27
28
|
},
|
|
28
29
|
isString
|
|
29
30
|
} = require('@contrast/common');
|
|
31
|
+
const { InstrumentationType: { RULE } } = require('../../../../constants');
|
|
30
32
|
const { createSubsetTags } = require('../../../tag-utils');
|
|
31
33
|
const { filterSafeTags, patchType } = require('../../common');
|
|
32
34
|
|
|
33
|
-
const ruleId = 'unvalidated-redirect';
|
|
34
|
-
|
|
35
35
|
const getURLArgument = (args) => {
|
|
36
36
|
if (!Array.isArray(args)) {
|
|
37
37
|
return { index: null, url: undefined };
|
|
@@ -51,13 +51,21 @@ const getURLArgument = (args) => {
|
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* @param {{
|
|
57
|
+
* assess: import('@contrast/assess').Assess,
|
|
58
|
+
* config: import('@contrast/config').Config,
|
|
59
|
+
* }} core
|
|
60
|
+
* @returns {import('@contrast/common').Installable}
|
|
61
|
+
*/
|
|
54
62
|
module.exports = function(core) {
|
|
55
63
|
const {
|
|
56
64
|
config,
|
|
57
65
|
depHooks,
|
|
58
66
|
patcher,
|
|
59
|
-
scopes: { sources },
|
|
60
67
|
assess: {
|
|
68
|
+
getSourceContext,
|
|
61
69
|
eventFactory: { createSinkEvent },
|
|
62
70
|
dataflow: {
|
|
63
71
|
tracker,
|
|
@@ -85,8 +93,7 @@ module.exports = function(core) {
|
|
|
85
93
|
name,
|
|
86
94
|
patchType,
|
|
87
95
|
post(data) {
|
|
88
|
-
|
|
89
|
-
if (!assessStore) return;
|
|
96
|
+
if (!getSourceContext(RULE, ruleId)) return;
|
|
90
97
|
|
|
91
98
|
const { url, index: valueIndex } = getURLArgument(data.args);
|
|
92
99
|
if (!url || !isString(url)) return;
|
|
@@ -16,9 +16,6 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
const { patchType } = require('../common');
|
|
18
18
|
const {
|
|
19
|
-
FS_METHODS,
|
|
20
|
-
Rule,
|
|
21
|
-
isString,
|
|
22
19
|
DataflowTag: {
|
|
23
20
|
URL_ENCODED,
|
|
24
21
|
LIMITED_CHARS,
|
|
@@ -26,16 +23,20 @@ const {
|
|
|
26
23
|
SAFE_PATH,
|
|
27
24
|
UNTRUSTED,
|
|
28
25
|
},
|
|
26
|
+
FS_METHODS,
|
|
27
|
+
Rule: { PATH_TRAVERSAL: ruleId },
|
|
29
28
|
inspect,
|
|
29
|
+
isString,
|
|
30
30
|
join,
|
|
31
31
|
} = require('@contrast/common');
|
|
32
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
32
33
|
|
|
33
34
|
module.exports = function(core) {
|
|
34
35
|
const {
|
|
35
36
|
depHooks,
|
|
36
37
|
patcher,
|
|
37
|
-
scopes: { instrumentation, sources },
|
|
38
38
|
assess: {
|
|
39
|
+
getSourceContext,
|
|
39
40
|
eventFactory: { createSinkEvent },
|
|
40
41
|
dataflow: {
|
|
41
42
|
tracker,
|
|
@@ -61,8 +62,7 @@ module.exports = function(core) {
|
|
|
61
62
|
|
|
62
63
|
const pre = (name, method, moduleName = 'fs', fullMethodName = '') => (data) => {
|
|
63
64
|
const { name: methodName, indices } = method;
|
|
64
|
-
|
|
65
|
-
if (!store || instrumentation.isLocked()) return;
|
|
65
|
+
if (!getSourceContext(RULE, ruleId)) return;
|
|
66
66
|
|
|
67
67
|
const values = getValues(indices, data.args);
|
|
68
68
|
if (!values.length) return;
|
|
@@ -105,7 +105,7 @@ module.exports = function(core) {
|
|
|
105
105
|
|
|
106
106
|
if (event) {
|
|
107
107
|
reportFindings({
|
|
108
|
-
ruleId
|
|
108
|
+
ruleId,
|
|
109
109
|
sinkEvent: event,
|
|
110
110
|
});
|
|
111
111
|
}
|
|
@@ -27,8 +27,9 @@ const {
|
|
|
27
27
|
CUSTOM_VALIDATED,
|
|
28
28
|
LIMITED_CHARS,
|
|
29
29
|
},
|
|
30
|
-
Rule: { UNSAFE_CODE_EXECUTION }
|
|
30
|
+
Rule: { UNSAFE_CODE_EXECUTION: ruleId }
|
|
31
31
|
} = require('@contrast/common');
|
|
32
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
32
33
|
const { patchType, filterSafeTags } = require('../common');
|
|
33
34
|
|
|
34
35
|
const safeTags = [
|
|
@@ -44,8 +45,8 @@ module.exports = function (core) {
|
|
|
44
45
|
config,
|
|
45
46
|
logger,
|
|
46
47
|
patcher,
|
|
47
|
-
scopes: { sources, instrumentation },
|
|
48
48
|
assess: {
|
|
49
|
+
getSourceContext,
|
|
49
50
|
eventFactory: { createSinkEvent },
|
|
50
51
|
dataflow: {
|
|
51
52
|
tracker,
|
|
@@ -65,15 +66,10 @@ module.exports = function (core) {
|
|
|
65
66
|
name: 'global.ContrastMethods.Function',
|
|
66
67
|
patchType,
|
|
67
68
|
pre({ args: origArgs, hooked, orig, name }) {
|
|
68
|
-
|
|
69
|
+
if (!getSourceContext(RULE, ruleId)) return;
|
|
70
|
+
|
|
69
71
|
const fnBody = origArgs[origArgs.length - 1];
|
|
70
|
-
if (
|
|
71
|
-
!store ||
|
|
72
|
-
instrumentation.isLocked() ||
|
|
73
|
-
!fnBody ||
|
|
74
|
-
!isString(fnBody)
|
|
75
|
-
)
|
|
76
|
-
return;
|
|
72
|
+
if (!fnBody || !isString(fnBody)) return;
|
|
77
73
|
|
|
78
74
|
const strInfo = tracker.getData(fnBody);
|
|
79
75
|
|
|
@@ -90,7 +86,7 @@ module.exports = function (core) {
|
|
|
90
86
|
|
|
91
87
|
reportSafePositive({
|
|
92
88
|
name,
|
|
93
|
-
ruleId
|
|
89
|
+
ruleId,
|
|
94
90
|
safeTags: foundSafeTags,
|
|
95
91
|
strInfo: safeStrInfo,
|
|
96
92
|
});
|
|
@@ -142,7 +138,7 @@ module.exports = function (core) {
|
|
|
142
138
|
|
|
143
139
|
if (event) {
|
|
144
140
|
reportFindings({
|
|
145
|
-
ruleId
|
|
141
|
+
ruleId,
|
|
146
142
|
sinkEvent: event,
|
|
147
143
|
});
|
|
148
144
|
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
+
const Url = require('url');
|
|
18
19
|
const {
|
|
19
20
|
inspect,
|
|
20
21
|
isString,
|
|
@@ -25,19 +26,27 @@ const {
|
|
|
25
26
|
CUSTOM_VALIDATED_SSRF,
|
|
26
27
|
CUSTOM_VALIDATED,
|
|
27
28
|
LIMITED_CHARS
|
|
28
|
-
}
|
|
29
|
+
},
|
|
30
|
+
Rule: { SSRF: ruleId },
|
|
29
31
|
} = require('@contrast/common');
|
|
30
|
-
const
|
|
31
|
-
const trustedLibs = [/^(?!.*(newrelic)).*http.*$/];
|
|
32
|
-
const { patchType } = require('../../common');
|
|
32
|
+
const { InstrumentationType: { RULE } } = require('../../../../constants');
|
|
33
33
|
const { createAppendTags } = require('../../../tag-utils');
|
|
34
|
+
const { patchType } = require('../../common');
|
|
35
|
+
|
|
36
|
+
const trustedLibs = [/^(?!.*(newrelic)).*http.*$/];
|
|
34
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {{
|
|
40
|
+
* assess: import('@contrast/assess').Assess,
|
|
41
|
+
* }} core
|
|
42
|
+
* @returns {import('@contrast/common').Installable}
|
|
43
|
+
*/
|
|
35
44
|
module.exports = function(core) {
|
|
36
45
|
const {
|
|
37
46
|
depHooks,
|
|
38
47
|
patcher,
|
|
39
|
-
scopes: { sources },
|
|
40
48
|
assess: {
|
|
49
|
+
getSourceContext,
|
|
41
50
|
eventFactory: { createSinkEvent },
|
|
42
51
|
dataflow: {
|
|
43
52
|
tracker,
|
|
@@ -92,8 +101,7 @@ module.exports = function(core) {
|
|
|
92
101
|
name,
|
|
93
102
|
patchType,
|
|
94
103
|
pre(data) {
|
|
95
|
-
|
|
96
|
-
if (!sourceContext) return;
|
|
104
|
+
if (!getSourceContext(RULE, ruleId)) return;
|
|
97
105
|
|
|
98
106
|
const [req] = data.args;
|
|
99
107
|
if (!req) return;
|
|
@@ -136,7 +144,7 @@ module.exports = function(core) {
|
|
|
136
144
|
|
|
137
145
|
if (event) {
|
|
138
146
|
reportFindings({
|
|
139
|
-
ruleId
|
|
147
|
+
ruleId,
|
|
140
148
|
sinkEvent: event
|
|
141
149
|
});
|
|
142
150
|
}
|
|
@@ -31,15 +31,24 @@ const {
|
|
|
31
31
|
},
|
|
32
32
|
Rule: { REFLECTED_XSS: ruleId },
|
|
33
33
|
} = require('@contrast/common');
|
|
34
|
+
const { InstrumentationType: { RULE } } = require('../../../../constants');
|
|
35
|
+
|
|
34
36
|
const { patchType, filterSafeTags } = require('../../common');
|
|
35
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {{
|
|
40
|
+
* assess: import('@contrast/assess').Assess,
|
|
41
|
+
* config: import('@contrast/config').Config,
|
|
42
|
+
* }} core
|
|
43
|
+
* @returns {import('@contrast/common').Installable}
|
|
44
|
+
*/
|
|
36
45
|
module.exports = function(core) {
|
|
37
46
|
const {
|
|
38
47
|
config,
|
|
39
48
|
depHooks,
|
|
40
49
|
patcher,
|
|
41
|
-
scopes: { sources },
|
|
42
50
|
assess: {
|
|
51
|
+
getSourceContext,
|
|
43
52
|
eventFactory: { createSinkEvent },
|
|
44
53
|
dataflow: {
|
|
45
54
|
tracker,
|
|
@@ -68,7 +77,7 @@ module.exports = function(core) {
|
|
|
68
77
|
];
|
|
69
78
|
|
|
70
79
|
const preHook = (name, method) => (data) => {
|
|
71
|
-
const sourceContext =
|
|
80
|
+
const sourceContext = getSourceContext(RULE, ruleId);
|
|
72
81
|
if (!sourceContext) return;
|
|
73
82
|
|
|
74
83
|
const payload = data.args[0];
|
|
@@ -25,20 +25,29 @@ const {
|
|
|
25
25
|
LIMITED_CHARS,
|
|
26
26
|
URL_ENCODED,
|
|
27
27
|
},
|
|
28
|
+
Rule: { UNVALIDATED_REDIRECT: ruleId },
|
|
28
29
|
isString
|
|
29
30
|
} = require('@contrast/common');
|
|
31
|
+
const { InstrumentationType: { RULE } } = require('../../../../constants');
|
|
30
32
|
const { createSubsetTags } = require('../../../tag-utils');
|
|
31
33
|
const { filterSafeTags, patchType } = require('../../common');
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @param {{
|
|
37
|
+
* assess: import('@contrast/assess').Assess,
|
|
38
|
+
* config: import('@contrast/config').Config,
|
|
39
|
+
* logger: import('@contrast/logger').Logger,
|
|
40
|
+
* messages: import('@contrast/common').Messages,
|
|
41
|
+
* }} core
|
|
42
|
+
* @returns {import('@contrast/common').Installable}
|
|
43
|
+
*/
|
|
35
44
|
module.exports = function(core) {
|
|
36
45
|
const {
|
|
37
46
|
depHooks,
|
|
38
47
|
patcher,
|
|
39
48
|
config,
|
|
40
|
-
scopes: { sources },
|
|
41
49
|
assess: {
|
|
50
|
+
getSourceContext,
|
|
42
51
|
eventFactory: { createSinkEvent },
|
|
43
52
|
dataflow: {
|
|
44
53
|
tracker,
|
|
@@ -46,11 +55,8 @@ module.exports = function(core) {
|
|
|
46
55
|
},
|
|
47
56
|
},
|
|
48
57
|
} = core;
|
|
49
|
-
const unvalidatedRedirect =
|
|
50
|
-
(core.assess.dataflow.sinks.koa.unvalidatedRedirect = {});
|
|
51
58
|
|
|
52
59
|
const inspect = patcher.unwrap(util.inspect);
|
|
53
|
-
|
|
54
60
|
const safeTags = [
|
|
55
61
|
CUSTOM_ENCODED,
|
|
56
62
|
CUSTOM_VALIDATED,
|
|
@@ -59,6 +65,8 @@ module.exports = function(core) {
|
|
|
59
65
|
URL_ENCODED,
|
|
60
66
|
];
|
|
61
67
|
|
|
68
|
+
const unvalidatedRedirect = core.assess.dataflow.sinks.koa.unvalidatedRedirect = {};
|
|
69
|
+
|
|
62
70
|
unvalidatedRedirect.install = function() {
|
|
63
71
|
depHooks.resolve({ name: 'koa', file: 'lib/response', version: '<2.9.0' }, (Response) => {
|
|
64
72
|
const name = 'Koa.Response.redirect';
|
|
@@ -66,8 +74,7 @@ module.exports = function(core) {
|
|
|
66
74
|
name,
|
|
67
75
|
patchType,
|
|
68
76
|
pre(data) {
|
|
69
|
-
|
|
70
|
-
if (!assessStore) return;
|
|
77
|
+
if (!getSourceContext(RULE, ruleId)) return;
|
|
71
78
|
|
|
72
79
|
let isBackRoute = false;
|
|
73
80
|
let [url] = data.args;
|
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { patchType } = require('../common');
|
|
19
18
|
const {
|
|
20
|
-
Rule: { XXE },
|
|
19
|
+
Rule: { XXE: ruleId },
|
|
21
20
|
isString,
|
|
22
21
|
DataflowTag: {
|
|
23
22
|
UNTRUSTED,
|
|
@@ -26,18 +25,29 @@ const {
|
|
|
26
25
|
},
|
|
27
26
|
inspect
|
|
28
27
|
} = require('@contrast/common');
|
|
28
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
29
|
+
const { patchType } = require('../common');
|
|
29
30
|
|
|
30
31
|
const safeTags = [
|
|
31
32
|
LIMITED_CHARS,
|
|
32
33
|
ALPHANUM_SPACE_HYPHEN
|
|
33
34
|
];
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @param {{
|
|
38
|
+
* assess: import('@contrast/assess').Assess,
|
|
39
|
+
* config: import('@contrast/config').Config,
|
|
40
|
+
* logger: import('@contrast/logger').Logger,
|
|
41
|
+
* messages: import('@contrast/common').Messages,
|
|
42
|
+
* }} core
|
|
43
|
+
* @returns {import('@contrast/common').Installable}
|
|
44
|
+
*/
|
|
35
45
|
module.exports = function(core) {
|
|
36
46
|
const {
|
|
37
47
|
depHooks,
|
|
38
48
|
patcher,
|
|
39
|
-
scopes: { sources, instrumentation },
|
|
40
49
|
assess: {
|
|
50
|
+
getSourceContext,
|
|
41
51
|
eventFactory: { createSinkEvent },
|
|
42
52
|
dataflow: {
|
|
43
53
|
tracker,
|
|
@@ -60,12 +70,7 @@ module.exports = function(core) {
|
|
|
60
70
|
name: `${moduleName}.${method}`,
|
|
61
71
|
patchType,
|
|
62
72
|
pre(data) {
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
!store ||
|
|
66
|
-
!data.args[0] ||
|
|
67
|
-
instrumentation.isLocked()
|
|
68
|
-
) return;
|
|
73
|
+
if (!getSourceContext(RULE, ruleId) || !data.args[0]) return;
|
|
69
74
|
|
|
70
75
|
const [xmlString, opts] = data.args;
|
|
71
76
|
|
|
@@ -104,7 +109,7 @@ module.exports = function(core) {
|
|
|
104
109
|
|
|
105
110
|
if (event) {
|
|
106
111
|
reportFindings({
|
|
107
|
-
ruleId
|
|
112
|
+
ruleId,
|
|
108
113
|
sinkEvent: event,
|
|
109
114
|
});
|
|
110
115
|
}
|
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const util = require('util');
|
|
18
|
-
const { patchType } = require('../common');
|
|
19
18
|
const {
|
|
20
19
|
traverseValues,
|
|
21
|
-
Rule,
|
|
20
|
+
Rule: { NOSQL_INJECTION_MONGO: ruleId },
|
|
22
21
|
DataflowTag: {
|
|
23
22
|
ALPHANUM_SPACE_HYPHEN,
|
|
24
23
|
LIMITED_CHARS,
|
|
@@ -27,6 +26,8 @@ const {
|
|
|
27
26
|
CUSTOM_VALIDATED_NOSQL_INJECTION,
|
|
28
27
|
},
|
|
29
28
|
} = require('@contrast/common');
|
|
29
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
30
|
+
const { patchType } = require('../common');
|
|
30
31
|
|
|
31
32
|
const collectionMethods = ['find', 'findOne', 'update', 'remove'];
|
|
32
33
|
const querySafeTags = [
|
|
@@ -36,13 +37,20 @@ const querySafeTags = [
|
|
|
36
37
|
CUSTOM_VALIDATED_NOSQL_INJECTION,
|
|
37
38
|
];
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @param {{
|
|
42
|
+
* assess: import('@contrast/assess').Assess,
|
|
43
|
+
* logger: import('@contrast/logger').Logger,
|
|
44
|
+
* }} core
|
|
45
|
+
* @returns {import('@contrast/common').Installable}
|
|
46
|
+
*/
|
|
39
47
|
module.exports = function(core) {
|
|
40
48
|
const {
|
|
41
49
|
depHooks,
|
|
42
50
|
logger,
|
|
43
51
|
patcher,
|
|
44
|
-
scopes: { sources, instrumentation },
|
|
45
52
|
assess: {
|
|
53
|
+
getSourceContext,
|
|
46
54
|
eventFactory: { createSinkEvent },
|
|
47
55
|
dataflow: {
|
|
48
56
|
tracker,
|
|
@@ -82,10 +90,7 @@ module.exports = function(core) {
|
|
|
82
90
|
name,
|
|
83
91
|
patchType,
|
|
84
92
|
around(next, data) {
|
|
85
|
-
|
|
86
|
-
if (!sourceCtx || instrumentation.isLocked()) {
|
|
87
|
-
return next();
|
|
88
|
-
}
|
|
93
|
+
if (!getSourceContext(RULE, ruleId)) return next();
|
|
89
94
|
|
|
90
95
|
const argIdx = 0;
|
|
91
96
|
const result = getVulnerabilityInfo(data.args[argIdx]);
|
|
@@ -120,7 +125,7 @@ module.exports = function(core) {
|
|
|
120
125
|
});
|
|
121
126
|
|
|
122
127
|
if (sinkEvent) {
|
|
123
|
-
reportFindings({ ruleId
|
|
128
|
+
reportFindings({ ruleId, sinkEvent });
|
|
124
129
|
}
|
|
125
130
|
|
|
126
131
|
return next();
|
|
@@ -24,12 +24,13 @@ const {
|
|
|
24
24
|
LIMITED_CHARS,
|
|
25
25
|
STRING_TYPE_CHECKED,
|
|
26
26
|
},
|
|
27
|
-
Rule: { NOSQL_INJECTION_MONGO },
|
|
27
|
+
Rule: { NOSQL_INJECTION_MONGO: ruleId },
|
|
28
28
|
isNonEmptyObject,
|
|
29
29
|
traverseValues,
|
|
30
30
|
isString,
|
|
31
31
|
inspect
|
|
32
32
|
} = require('@contrast/common');
|
|
33
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
33
34
|
const utils = require('../../tag-utils');
|
|
34
35
|
const { patchType, filterSafeTags } = require('../common');
|
|
35
36
|
|
|
@@ -66,19 +67,28 @@ const querySafeTags = [
|
|
|
66
67
|
STRING_TYPE_CHECKED,
|
|
67
68
|
];
|
|
68
69
|
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param {{
|
|
73
|
+
* assess: import('@contrast/assess').Assess,
|
|
74
|
+
* config: import('@contrast/config').Config,
|
|
75
|
+
* }} core
|
|
76
|
+
* @returns {import('@contrast/common').Installable}
|
|
77
|
+
*/
|
|
69
78
|
module.exports = function(core) {
|
|
70
79
|
const {
|
|
71
80
|
config,
|
|
72
81
|
depHooks,
|
|
73
82
|
logger,
|
|
74
83
|
patcher,
|
|
75
|
-
scopes: { sources, instrumentation },
|
|
76
84
|
assess: {
|
|
85
|
+
getSourceContext,
|
|
77
86
|
eventFactory: { createSinkEvent },
|
|
78
87
|
dataflow: {
|
|
79
88
|
tracker,
|
|
80
|
-
sinks: { isVulnerable,
|
|
81
|
-
}
|
|
89
|
+
sinks: { isVulnerable, reportFindings, reportSafePositive }
|
|
90
|
+
},
|
|
91
|
+
ruleScopes,
|
|
82
92
|
}
|
|
83
93
|
} = core;
|
|
84
94
|
|
|
@@ -228,17 +238,13 @@ module.exports = function(core) {
|
|
|
228
238
|
function createAroundHook(entity, name, method, getInfoMethod, vulnerableArgIdxs) {
|
|
229
239
|
const argsIdxsToCheck = vulnerableArgIdxs || [0];
|
|
230
240
|
return function(next, data) {
|
|
231
|
-
|
|
232
|
-
const sourceCtx = sources.getStore()?.assess;
|
|
233
|
-
|
|
234
|
-
if (isLocked(NOSQL_INJECTION_MONGO) || instrumentation.isLocked() || !sourceCtx) {
|
|
235
|
-
return next();
|
|
236
|
-
}
|
|
241
|
+
if (!getSourceContext(RULE, ruleId)) return next();
|
|
237
242
|
|
|
243
|
+
const { obj, args: origArgs } = data;
|
|
244
|
+
const safeReports = [];
|
|
238
245
|
let vulnInfo;
|
|
239
246
|
let reportSafe;
|
|
240
247
|
let vulnArgIdx;
|
|
241
|
-
const safeReports = [];
|
|
242
248
|
|
|
243
249
|
try {
|
|
244
250
|
for (const argIdx of argsIdxsToCheck) {
|
|
@@ -272,13 +278,15 @@ module.exports = function(core) {
|
|
|
272
278
|
|
|
273
279
|
reportSafePositive({
|
|
274
280
|
name,
|
|
275
|
-
ruleId
|
|
281
|
+
ruleId,
|
|
276
282
|
safeTags,
|
|
277
283
|
strInfo: strInfo.length === 1 ? strInfo[0] : strInfo
|
|
278
284
|
});
|
|
279
285
|
}
|
|
280
286
|
|
|
281
|
-
return methodsWithNestedCalls.includes(method)
|
|
287
|
+
return methodsWithNestedCalls.includes(method)
|
|
288
|
+
? ruleScopes.run(ruleId, async () => await next())
|
|
289
|
+
: next();
|
|
282
290
|
}
|
|
283
291
|
|
|
284
292
|
const { path, strInfo } = vulnInfo;
|
|
@@ -313,13 +321,15 @@ module.exports = function(core) {
|
|
|
313
321
|
});
|
|
314
322
|
|
|
315
323
|
if (sinkEvent) {
|
|
316
|
-
reportFindings({ ruleId
|
|
324
|
+
reportFindings({ ruleId, sinkEvent });
|
|
317
325
|
}
|
|
318
326
|
} catch (err) {
|
|
319
327
|
core.logger.error({ name, err }, 'assess sink analysis failed');
|
|
320
328
|
}
|
|
321
329
|
|
|
322
|
-
return methodsWithNestedCalls.includes(method)
|
|
330
|
+
return methodsWithNestedCalls.includes(method)
|
|
331
|
+
? ruleScopes.run(ruleId, async () => await next())
|
|
332
|
+
: next();
|
|
323
333
|
};
|
|
324
334
|
}
|
|
325
335
|
|
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const {
|
|
19
|
-
DataflowTag: {
|
|
20
|
-
|
|
19
|
+
DataflowTag: {
|
|
20
|
+
CUSTOM_VALIDATED,
|
|
21
|
+
CUSTOM_ENCODED,
|
|
22
|
+
LIMITED_CHARS,
|
|
23
|
+
SQL_ENCODED,
|
|
24
|
+
UNTRUSTED,
|
|
25
|
+
},
|
|
26
|
+
Rule: { SQL_INJECTION: ruleId },
|
|
21
27
|
isString
|
|
22
28
|
} = require('@contrast/common');
|
|
29
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
23
30
|
const { createModuleLabel } = require('../../propagation/common');
|
|
24
31
|
const { patchType, filterSafeTags } = require('../common');
|
|
25
32
|
|
|
@@ -30,15 +37,20 @@ const safeTags = [
|
|
|
30
37
|
CUSTOM_ENCODED,
|
|
31
38
|
];
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
/**
|
|
41
|
+
* @param {{
|
|
42
|
+
* assess: import('@contrast/assess').Assess,
|
|
43
|
+
* config: import('@contrast/config').Config,
|
|
44
|
+
* }} core
|
|
45
|
+
* @returns {import('@contrast/common').Installable}
|
|
46
|
+
*/
|
|
35
47
|
module.exports = function(core) {
|
|
36
48
|
const {
|
|
37
49
|
depHooks,
|
|
38
50
|
patcher,
|
|
39
51
|
config,
|
|
40
|
-
scopes: { sources },
|
|
41
52
|
assess: {
|
|
53
|
+
getSourceContext,
|
|
42
54
|
eventFactory: { createSinkEvent },
|
|
43
55
|
dataflow: {
|
|
44
56
|
tracker,
|
|
@@ -48,12 +60,11 @@ module.exports = function(core) {
|
|
|
48
60
|
} = core;
|
|
49
61
|
|
|
50
62
|
const pre = (name, method, obj, version) => (data) => {
|
|
51
|
-
const store = sources.getStore()?.assess;
|
|
52
63
|
if (
|
|
53
|
-
!
|
|
64
|
+
!getSourceContext(RULE, ruleId) ||
|
|
54
65
|
!data.args[0] ||
|
|
55
66
|
!isString(data.args[0]) ||
|
|
56
|
-
isLocked(
|
|
67
|
+
isLocked(ruleId)
|
|
57
68
|
) return;
|
|
58
69
|
|
|
59
70
|
const strInfo = tracker.getData(data.args[0]);
|
|
@@ -86,7 +97,7 @@ module.exports = function(core) {
|
|
|
86
97
|
|
|
87
98
|
if (event) {
|
|
88
99
|
reportFindings({
|
|
89
|
-
ruleId
|
|
100
|
+
ruleId,
|
|
90
101
|
sinkEvent: event,
|
|
91
102
|
});
|
|
92
103
|
}
|