@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,8 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
const { patchType } = require('../common');
|
|
19
19
|
const {
|
|
20
|
-
Rule: { SQL_INJECTION },
|
|
21
|
-
isString,
|
|
20
|
+
Rule: { SQL_INJECTION: ruleId },
|
|
22
21
|
DataflowTag: {
|
|
23
22
|
CUSTOM_ENCODED_SQL_INJECTION,
|
|
24
23
|
CUSTOM_ENCODED,
|
|
@@ -28,8 +27,10 @@ const {
|
|
|
28
27
|
LIMITED_CHARS,
|
|
29
28
|
UNTRUSTED
|
|
30
29
|
},
|
|
31
|
-
|
|
30
|
+
isString,
|
|
31
|
+
inspect,
|
|
32
32
|
} = require('@contrast/common');
|
|
33
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
33
34
|
|
|
34
35
|
const safeTags = [
|
|
35
36
|
CUSTOM_ENCODED_SQL_INJECTION,
|
|
@@ -40,12 +41,19 @@ const safeTags = [
|
|
|
40
41
|
LIMITED_CHARS,
|
|
41
42
|
];
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @param {{
|
|
46
|
+
* assess: import('@contrast/assess').Assess,
|
|
47
|
+
* config: import('@contrast/config').Config,
|
|
48
|
+
* }} core
|
|
49
|
+
* @returns {import('@contrast/common').Installable}
|
|
50
|
+
*/
|
|
43
51
|
module.exports = function(core) {
|
|
44
52
|
const {
|
|
45
53
|
depHooks,
|
|
46
54
|
patcher,
|
|
47
|
-
scopes: { sources },
|
|
48
55
|
assess: {
|
|
56
|
+
getSourceContext,
|
|
49
57
|
eventFactory: { createSinkEvent },
|
|
50
58
|
dataflow: {
|
|
51
59
|
tracker,
|
|
@@ -65,11 +73,10 @@ module.exports = function(core) {
|
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
const pre = (module, file, obj, method) => (data) => {
|
|
68
|
-
const store = sources.getStore()?.assess;
|
|
69
76
|
if (
|
|
70
|
-
!
|
|
77
|
+
!getSourceContext(RULE, ruleId) ||
|
|
71
78
|
!data.args[0] ||
|
|
72
|
-
isLocked(
|
|
79
|
+
isLocked(ruleId)
|
|
73
80
|
) return;
|
|
74
81
|
|
|
75
82
|
const val = getValueFromArgs(data.args);
|
|
@@ -106,7 +113,7 @@ module.exports = function(core) {
|
|
|
106
113
|
|
|
107
114
|
if (event) {
|
|
108
115
|
reportFindings({
|
|
109
|
-
ruleId
|
|
116
|
+
ruleId,
|
|
110
117
|
sinkEvent: event,
|
|
111
118
|
});
|
|
112
119
|
}
|
|
@@ -15,21 +15,29 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { patchType } = require('../common');
|
|
19
18
|
const {
|
|
20
|
-
Rule: { UNTRUSTED_DESERIALIZATION },
|
|
19
|
+
Rule: { UNTRUSTED_DESERIALIZATION: ruleId },
|
|
21
20
|
isString,
|
|
22
21
|
DataflowTag: {
|
|
23
22
|
UNTRUSTED
|
|
24
23
|
}
|
|
25
24
|
} = require('@contrast/common');
|
|
25
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
26
|
+
const { patchType } = require('../common');
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param {{
|
|
30
|
+
* assess: import('@contrast/assess').Assess,
|
|
31
|
+
* config: import('@contrast/config').Config,
|
|
32
|
+
* }} core
|
|
33
|
+
* @returns {import('@contrast/common').Installable}
|
|
34
|
+
*/
|
|
27
35
|
module.exports = function(core) {
|
|
28
36
|
const {
|
|
29
37
|
depHooks,
|
|
30
38
|
patcher,
|
|
31
|
-
scopes: { sources, instrumentation },
|
|
32
39
|
assess: {
|
|
40
|
+
getSourceContext,
|
|
33
41
|
eventFactory: { createSinkEvent },
|
|
34
42
|
dataflow: {
|
|
35
43
|
tracker,
|
|
@@ -45,23 +53,13 @@ module.exports = function(core) {
|
|
|
45
53
|
name: 'node-serialize.unserialize',
|
|
46
54
|
patchType,
|
|
47
55
|
pre(data) {
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
!store ||
|
|
51
|
-
!data.args[0] ||
|
|
52
|
-
instrumentation.isLocked()
|
|
53
|
-
) return;
|
|
56
|
+
if (!getSourceContext(RULE, ruleId) || !data.args[0]) return;
|
|
54
57
|
|
|
55
58
|
const [input] = data.args;
|
|
56
|
-
|
|
57
|
-
if (!isString(input)) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
59
|
+
if (!isString(input)) return;
|
|
60
60
|
|
|
61
61
|
const strInfo = tracker.getData(input);
|
|
62
|
-
if (!strInfo || !isVulnerable(UNTRUSTED, [], strInfo.tags))
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
62
|
+
if (!strInfo || !isVulnerable(UNTRUSTED, [], strInfo.tags)) return;
|
|
65
63
|
|
|
66
64
|
const sinkEvent = createSinkEvent({
|
|
67
65
|
name: 'node-serialize.unserialize',
|
|
@@ -89,7 +87,7 @@ module.exports = function(core) {
|
|
|
89
87
|
|
|
90
88
|
if (sinkEvent) {
|
|
91
89
|
reportFindings({
|
|
92
|
-
ruleId
|
|
90
|
+
ruleId,
|
|
93
91
|
sinkEvent,
|
|
94
92
|
});
|
|
95
93
|
}
|
|
@@ -17,19 +17,33 @@
|
|
|
17
17
|
|
|
18
18
|
const util = require('util');
|
|
19
19
|
const {
|
|
20
|
-
DataflowTag: {
|
|
20
|
+
DataflowTag: {
|
|
21
|
+
CUSTOM_VALIDATED,
|
|
22
|
+
CUSTOM_ENCODED,
|
|
23
|
+
LIMITED_CHARS,
|
|
24
|
+
SQL_ENCODED,
|
|
25
|
+
UNTRUSTED,
|
|
26
|
+
},
|
|
21
27
|
Rule: { SQL_INJECTION: ruleId },
|
|
22
28
|
isString,
|
|
23
29
|
} = require('@contrast/common');
|
|
30
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
24
31
|
const { filterSafeTags, patchType } = require('../common');
|
|
25
32
|
|
|
33
|
+
/**
|
|
34
|
+
* @param {{
|
|
35
|
+
* assess: import('@contrast/assess').Assess,
|
|
36
|
+
* config: import('@contrast/config').Config,
|
|
37
|
+
* }} core
|
|
38
|
+
* @returns {import('@contrast/common').Installable}
|
|
39
|
+
*/
|
|
26
40
|
module.exports = function(core) {
|
|
27
41
|
const {
|
|
28
42
|
config,
|
|
29
43
|
depHooks,
|
|
30
44
|
patcher,
|
|
31
|
-
scopes: { sources },
|
|
32
45
|
assess: {
|
|
46
|
+
getSourceContext,
|
|
33
47
|
eventFactory: { createSinkEvent },
|
|
34
48
|
dataflow: {
|
|
35
49
|
tracker,
|
|
@@ -50,8 +64,7 @@ module.exports = function(core) {
|
|
|
50
64
|
const postgres = core.assess.dataflow.sinks.postgres = {};
|
|
51
65
|
|
|
52
66
|
const preHook = (methodSignature) => (data) => {
|
|
53
|
-
|
|
54
|
-
if (!assessStore || isLocked(ruleId)) return;
|
|
67
|
+
if (!getSourceContext(RULE, ruleId) || isLocked(ruleId)) return;
|
|
55
68
|
|
|
56
69
|
const [arg0] = data.args;
|
|
57
70
|
const query = arg0?.text || arg0;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
const util = require('util');
|
|
19
19
|
const {
|
|
20
|
-
Rule: { SQL_INJECTION },
|
|
20
|
+
Rule: { SQL_INJECTION: ruleId },
|
|
21
21
|
DataflowTag: {
|
|
22
22
|
UNTRUSTED,
|
|
23
23
|
SQL_ENCODED,
|
|
@@ -26,15 +26,23 @@ const {
|
|
|
26
26
|
CUSTOM_ENCODED,
|
|
27
27
|
},
|
|
28
28
|
} = require('@contrast/common');
|
|
29
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
29
30
|
const { patchType, filterSafeTags } = require('../common');
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* @param {{
|
|
34
|
+
* assess: import('@contrast/assess').Assess,
|
|
35
|
+
* config: import('@contrast/config').Config,
|
|
36
|
+
* }} core
|
|
37
|
+
* @returns {import('@contrast/common').Installable}
|
|
38
|
+
*/
|
|
31
39
|
module.exports = function(core) {
|
|
32
40
|
const {
|
|
33
41
|
depHooks,
|
|
34
42
|
patcher,
|
|
35
43
|
config,
|
|
36
|
-
scopes: { sources },
|
|
37
44
|
assess: {
|
|
45
|
+
getSourceContext,
|
|
38
46
|
eventFactory: { createSinkEvent },
|
|
39
47
|
dataflow: {
|
|
40
48
|
tracker,
|
|
@@ -61,10 +69,9 @@ module.exports = function(core) {
|
|
|
61
69
|
name: sequelizeQueryPatchName,
|
|
62
70
|
patchType,
|
|
63
71
|
around(next, data) {
|
|
64
|
-
|
|
65
|
-
const sourceContext = sources.getStore()?.assess;
|
|
66
|
-
if (!sourceContext || !args[0]) return next();
|
|
72
|
+
if (!getSourceContext(RULE, ruleId) || !data.args[0]) return next();
|
|
67
73
|
|
|
74
|
+
const { args, hooked, orig } = data;
|
|
68
75
|
const query = typeof args[0] === 'string' ? args[0] : args[0].query;
|
|
69
76
|
|
|
70
77
|
try {
|
|
@@ -74,7 +81,7 @@ module.exports = function(core) {
|
|
|
74
81
|
if (queryInfo && !isVulnerableQuery && config.assess.safe_positives.enable) {
|
|
75
82
|
reportSafePositive({
|
|
76
83
|
name: sequelizeQueryPatchName,
|
|
77
|
-
ruleId
|
|
84
|
+
ruleId,
|
|
78
85
|
safeTags: filterSafeTags(safeTags, queryInfo),
|
|
79
86
|
strInfo: {
|
|
80
87
|
value: queryInfo?.value,
|
|
@@ -87,7 +94,7 @@ module.exports = function(core) {
|
|
|
87
94
|
!queryInfo ||
|
|
88
95
|
!isVulnerableQuery
|
|
89
96
|
) {
|
|
90
|
-
return runInActiveSink(
|
|
97
|
+
return runInActiveSink(ruleId, async () => await next());
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
const sqlValue =
|
|
@@ -122,7 +129,7 @@ module.exports = function(core) {
|
|
|
122
129
|
|
|
123
130
|
if (event) {
|
|
124
131
|
reportFindings({
|
|
125
|
-
ruleId
|
|
132
|
+
ruleId,
|
|
126
133
|
sinkEvent: event,
|
|
127
134
|
});
|
|
128
135
|
}
|
|
@@ -134,7 +141,7 @@ module.exports = function(core) {
|
|
|
134
141
|
);
|
|
135
142
|
}
|
|
136
143
|
|
|
137
|
-
return runInActiveSink(
|
|
144
|
+
return runInActiveSink(ruleId, async () => await next());
|
|
138
145
|
},
|
|
139
146
|
});
|
|
140
147
|
});
|
|
@@ -17,10 +17,17 @@
|
|
|
17
17
|
|
|
18
18
|
const { patchType } = require('../common');
|
|
19
19
|
const {
|
|
20
|
-
DataflowTag: {
|
|
21
|
-
|
|
20
|
+
DataflowTag: {
|
|
21
|
+
CUSTOM_VALIDATED,
|
|
22
|
+
CUSTOM_ENCODED,
|
|
23
|
+
LIMITED_CHARS,
|
|
24
|
+
SQL_ENCODED,
|
|
25
|
+
UNTRUSTED,
|
|
26
|
+
},
|
|
27
|
+
Rule: { SQL_INJECTION: ruleId },
|
|
22
28
|
isString
|
|
23
29
|
} = require('@contrast/common');
|
|
30
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
24
31
|
|
|
25
32
|
const safeTags = [
|
|
26
33
|
SQL_ENCODED,
|
|
@@ -29,12 +36,19 @@ const safeTags = [
|
|
|
29
36
|
CUSTOM_ENCODED,
|
|
30
37
|
];
|
|
31
38
|
|
|
39
|
+
/**
|
|
40
|
+
* @param {{
|
|
41
|
+
* assess: import('@contrast/assess').Assess,
|
|
42
|
+
* config: import('@contrast/config').Config,
|
|
43
|
+
* }} core
|
|
44
|
+
* @returns {import('@contrast/common').Installable}
|
|
45
|
+
*/
|
|
32
46
|
module.exports = function(core) {
|
|
33
47
|
const {
|
|
34
48
|
depHooks,
|
|
35
49
|
patcher,
|
|
36
|
-
scopes: { sources },
|
|
37
50
|
assess: {
|
|
51
|
+
getSourceContext,
|
|
38
52
|
eventFactory: { createSinkEvent },
|
|
39
53
|
dataflow: {
|
|
40
54
|
tracker,
|
|
@@ -44,12 +58,11 @@ module.exports = function(core) {
|
|
|
44
58
|
} = core;
|
|
45
59
|
|
|
46
60
|
const pre = (name, method) => (data) => {
|
|
47
|
-
const store = sources.getStore()?.assess;
|
|
48
61
|
if (
|
|
49
|
-
!
|
|
62
|
+
!getSourceContext(RULE, ruleId) ||
|
|
50
63
|
!data.args[0] ||
|
|
51
64
|
!isString(data.args[0]) ||
|
|
52
|
-
isLocked(
|
|
65
|
+
isLocked(ruleId)
|
|
53
66
|
) return;
|
|
54
67
|
|
|
55
68
|
const strInfo = tracker.getData(data.args[0]);
|
|
@@ -83,7 +96,7 @@ module.exports = function(core) {
|
|
|
83
96
|
|
|
84
97
|
if (event) {
|
|
85
98
|
reportFindings({
|
|
86
|
-
ruleId
|
|
99
|
+
ruleId,
|
|
87
100
|
sinkEvent: event,
|
|
88
101
|
});
|
|
89
102
|
}
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
const { patchType, filterSafeTags } = require('../common');
|
|
18
18
|
const {
|
|
19
|
-
isString,
|
|
20
19
|
DataflowTag: {
|
|
21
20
|
UNTRUSTED,
|
|
22
21
|
CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
|
|
@@ -25,14 +24,17 @@ const {
|
|
|
25
24
|
CUSTOM_VALIDATED,
|
|
26
25
|
LIMITED_CHARS,
|
|
27
26
|
},
|
|
28
|
-
Rule: { UNSAFE_CODE_EXECUTION },
|
|
29
|
-
isNonEmptyObject,
|
|
27
|
+
Rule: { UNSAFE_CODE_EXECUTION: ruleId },
|
|
30
28
|
inspect,
|
|
31
|
-
|
|
29
|
+
isNonEmptyObject,
|
|
30
|
+
isString,
|
|
32
31
|
join,
|
|
33
32
|
split,
|
|
33
|
+
traverseValues,
|
|
34
34
|
} = require('@contrast/common');
|
|
35
|
+
const { InstrumentationType: { RULE } } = require('../../../constants');
|
|
35
36
|
const { createAdjustedQueryTags } = require('../../tag-utils');
|
|
37
|
+
|
|
36
38
|
const safeTags = [
|
|
37
39
|
CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
|
|
38
40
|
CUSTOM_ENCODED,
|
|
@@ -41,13 +43,20 @@ const safeTags = [
|
|
|
41
43
|
LIMITED_CHARS,
|
|
42
44
|
];
|
|
43
45
|
|
|
46
|
+
/**
|
|
47
|
+
* @param {{
|
|
48
|
+
* assess: import('@contrast/assess').Assess,
|
|
49
|
+
* config: import('@contrast/config').Config,
|
|
50
|
+
* }} core
|
|
51
|
+
* @returns {import('@contrast/common').Installable}
|
|
52
|
+
*/
|
|
44
53
|
module.exports = function (core) {
|
|
45
54
|
const {
|
|
46
55
|
config,
|
|
47
56
|
depHooks,
|
|
48
57
|
patcher,
|
|
49
|
-
scopes: { sources, instrumentation },
|
|
50
58
|
assess: {
|
|
59
|
+
getSourceContext,
|
|
51
60
|
eventFactory: { createSinkEvent },
|
|
52
61
|
dataflow: {
|
|
53
62
|
tracker,
|
|
@@ -133,14 +142,7 @@ module.exports = function (core) {
|
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
function around(next, { args: origArgs, hooked, orig, name }) {
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
!store ||
|
|
139
|
-
instrumentation.isLocked() ||
|
|
140
|
-
isLocked(UNSAFE_CODE_EXECUTION)
|
|
141
|
-
) {
|
|
142
|
-
return next();
|
|
143
|
-
}
|
|
145
|
+
if (!getSourceContext(RULE, ruleId) || isLocked(ruleId)) return next();
|
|
144
146
|
|
|
145
147
|
const methodPath = split(name, '.');
|
|
146
148
|
const method = methodPath[methodPath.length - 1];
|
|
@@ -189,13 +191,13 @@ module.exports = function (core) {
|
|
|
189
191
|
|
|
190
192
|
reportSafePositive({
|
|
191
193
|
name,
|
|
192
|
-
ruleId
|
|
194
|
+
ruleId,
|
|
193
195
|
safeTags: Array.from(foundSafeTags),
|
|
194
196
|
strInfo,
|
|
195
197
|
});
|
|
196
198
|
|
|
197
199
|
return name === 'vm.runInNewContext'
|
|
198
|
-
? runInActiveSink(
|
|
200
|
+
? runInActiveSink(ruleId, () => next())
|
|
199
201
|
: next();
|
|
200
202
|
}
|
|
201
203
|
|
|
@@ -233,14 +235,14 @@ module.exports = function (core) {
|
|
|
233
235
|
|
|
234
236
|
if (event) {
|
|
235
237
|
reportFindings({
|
|
236
|
-
ruleId
|
|
238
|
+
ruleId,
|
|
237
239
|
sinkEvent: event,
|
|
238
240
|
});
|
|
239
241
|
}
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
return name === 'vm.runInNewContext'
|
|
243
|
-
? runInActiveSink(
|
|
245
|
+
? runInActiveSink(ruleId, () => next())
|
|
244
246
|
: next();
|
|
245
247
|
}
|
|
246
248
|
|
|
@@ -17,12 +17,17 @@
|
|
|
17
17
|
const { patchType } = require('../common');
|
|
18
18
|
const { toLowerCase, InputType } = require('@contrast/common');
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @param {{
|
|
22
|
+
* assess: import('@contrast/assess').Assess,
|
|
23
|
+
* }} core
|
|
24
|
+
*/
|
|
20
25
|
module.exports = function(core) {
|
|
21
26
|
const {
|
|
22
|
-
|
|
27
|
+
assess: { dataflow, makeSourceContext },
|
|
23
28
|
instrumentation: { instrument },
|
|
24
|
-
assess: { dataflow },
|
|
25
29
|
patcher,
|
|
30
|
+
scopes,
|
|
26
31
|
} = core;
|
|
27
32
|
|
|
28
33
|
const logger = core.logger.child('contrast:assess');
|
|
@@ -34,19 +39,20 @@ module.exports = function(core) {
|
|
|
34
39
|
function around(next, data) {
|
|
35
40
|
const [type] = data.args;
|
|
36
41
|
|
|
37
|
-
if (type !== 'request')
|
|
38
|
-
return next();
|
|
39
|
-
}
|
|
42
|
+
if (type !== 'request') return next();
|
|
40
43
|
|
|
41
44
|
try {
|
|
42
45
|
const [, req, res] = data.args;
|
|
43
46
|
const store = scopes.sources.getStore();
|
|
44
47
|
|
|
45
48
|
if (!store) {
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
// this would indicate that sources did not install correctly
|
|
50
|
+
throw new Error('async request store not found');
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
store.assess = makeSourceContext(req, res);
|
|
54
|
+
if (!store.assess) return;
|
|
55
|
+
|
|
50
56
|
patcher.patch(res, 'writeHead', {
|
|
51
57
|
name: 'write-head',
|
|
52
58
|
patchType,
|
|
@@ -86,27 +92,7 @@ module.exports = function(core) {
|
|
|
86
92
|
});
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
let uriPath, queries;
|
|
90
|
-
const ix = req.url.indexOf('?');
|
|
91
|
-
|
|
92
|
-
if (ix >= 0) {
|
|
93
|
-
uriPath = req.url.slice(0, ix);
|
|
94
|
-
queries = req.url.slice(ix + 1);
|
|
95
|
-
} else {
|
|
96
|
-
uriPath = req.url;
|
|
97
|
-
queries = '';
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const headers = {};
|
|
101
95
|
const sourceName = 'ClientRequest';
|
|
102
|
-
|
|
103
|
-
store.assess = {
|
|
104
|
-
responseData: {},
|
|
105
|
-
sourceEventsCount: 0,
|
|
106
|
-
propagationEventsCount: 0,
|
|
107
|
-
findings: {},
|
|
108
|
-
};
|
|
109
|
-
|
|
110
96
|
const sourceInfo = {
|
|
111
97
|
name: sourceName,
|
|
112
98
|
stacktraceOpts: {
|
|
@@ -141,24 +127,10 @@ module.exports = function(core) {
|
|
|
141
127
|
|
|
142
128
|
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
143
129
|
const header = toLowerCase(req.rawHeaders[i]);
|
|
144
|
-
headers[header] = req.rawHeaders[i + 1];
|
|
145
130
|
req.rawHeaders[i + 1] = req.headers[header];
|
|
146
131
|
}
|
|
147
|
-
|
|
148
|
-
const contentType = headers['content-type'] && toLowerCase(headers['content-type']);
|
|
149
|
-
|
|
150
|
-
store.assess.reqData = {
|
|
151
|
-
ip: req.socket.remoteAddress,
|
|
152
|
-
httpVersion: req.httpVersion,
|
|
153
|
-
method: req.method,
|
|
154
|
-
headers,
|
|
155
|
-
uriPath,
|
|
156
|
-
queries,
|
|
157
|
-
contentType,
|
|
158
|
-
};
|
|
159
|
-
|
|
160
132
|
} catch (err) {
|
|
161
|
-
logger.error({ err }, 'Error during
|
|
133
|
+
logger.error({ err }, 'Error during Assess request handling');
|
|
162
134
|
}
|
|
163
135
|
|
|
164
136
|
setImmediate(() => {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2023 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
const { patchType } = require('../../common');
|
|
18
|
+
const { InputType } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
logger,
|
|
24
|
+
patcher,
|
|
25
|
+
scopes,
|
|
26
|
+
assess: { dataflow: { sources } },
|
|
27
|
+
} = core;
|
|
28
|
+
|
|
29
|
+
function handler(req, constructorOpt) {
|
|
30
|
+
const sourceContext = scopes.sources.getStore()?.assess;
|
|
31
|
+
if (!sourceContext) return;
|
|
32
|
+
|
|
33
|
+
function handle(context, data, key) {
|
|
34
|
+
try {
|
|
35
|
+
sources.handle({
|
|
36
|
+
context,
|
|
37
|
+
data,
|
|
38
|
+
keys: [key],
|
|
39
|
+
name: 'multer',
|
|
40
|
+
inputType: InputType.BODY,
|
|
41
|
+
sourceContext,
|
|
42
|
+
stacktraceOpts: {
|
|
43
|
+
constructorOpt,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.error({ err }, 'error handling Koa multer Assess dataflow %s.%s source', context, key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (req.file) {
|
|
52
|
+
handle('req', req, 'file');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (Array.isArray(req.files)) {
|
|
56
|
+
for (let i = 0; i < req.files.length; i++) {
|
|
57
|
+
handle('req.files', req.files[i], i);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (req.body && Object.keys(req.body).length) {
|
|
62
|
+
handle('req', req, 'body');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function install() {
|
|
67
|
+
['koa-multer', '@koa/multer'].forEach((name) => {
|
|
68
|
+
depHooks.resolve(
|
|
69
|
+
{ name }, (_export) => {
|
|
70
|
+
const origMulter = _export;
|
|
71
|
+
return patcher.patch(_export, {
|
|
72
|
+
name,
|
|
73
|
+
patchType,
|
|
74
|
+
post(data) {
|
|
75
|
+
const { args, hooked } = data;
|
|
76
|
+
const instance = origMulter.apply(this, args);
|
|
77
|
+
const origMake = instance._makeMiddleware;
|
|
78
|
+
instance._makeMiddleware = function _makeMiddleware(...args) {
|
|
79
|
+
const origMulterMiddleware = origMake.apply(this, args);
|
|
80
|
+
return function multerMiddleware(req, res, origNext) {
|
|
81
|
+
|
|
82
|
+
const next = function(...args) {
|
|
83
|
+
handler(req, hooked);
|
|
84
|
+
return origNext.apply(this, args);
|
|
85
|
+
};
|
|
86
|
+
return origMulterMiddleware.apply(this, [req, res, next]);
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
data.result = instance;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const koaMulterInstrumentation = sources.koaInstrumentation.koaMulter = {
|
|
98
|
+
install
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return koaMulterInstrumentation;
|
|
102
|
+
};
|