@contrast/protect 1.2.1 → 1.4.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/error-handlers/constants.js +15 -0
- package/lib/error-handlers/index.js +17 -0
- package/lib/error-handlers/install/express4.js +89 -0
- package/lib/error-handlers/install/fastify3.js +17 -4
- package/lib/error-handlers/install/koa2.js +16 -2
- package/lib/esm-loader.mjs +15 -0
- package/lib/get-source-context.js +33 -0
- package/lib/hardening/constants.js +20 -0
- package/lib/hardening/handlers.js +65 -0
- package/lib/hardening/index.js +29 -0
- package/lib/hardening/install/node-serialize0.js +59 -0
- package/lib/index.d.ts +127 -19
- package/lib/index.js +19 -0
- package/lib/input-analysis/constants.js +20 -0
- package/lib/input-analysis/handlers.js +201 -16
- package/lib/input-analysis/index.js +40 -3
- package/lib/input-analysis/install/body-parser1.js +122 -0
- package/lib/input-analysis/install/cookie-parser1.js +80 -0
- package/lib/input-analysis/install/express4.js +103 -0
- package/lib/input-analysis/install/fastify3.js +51 -24
- package/lib/input-analysis/install/formidable1.js +72 -0
- package/lib/input-analysis/install/http.js +30 -4
- package/lib/input-analysis/install/koa-body5.js +63 -0
- package/lib/input-analysis/install/koa-bodyparser4.js +64 -0
- package/lib/input-analysis/install/koa2.js +38 -48
- package/lib/input-analysis/install/multer1.js +88 -0
- package/lib/input-analysis/install/qs6.js +57 -0
- package/lib/input-analysis/install/universal-cookie4.js +52 -0
- package/lib/input-analysis/ip-analysis.js +76 -0
- package/lib/input-analysis/virtual-patches.js +109 -0
- package/lib/input-tracing/constants.js +15 -0
- package/lib/input-tracing/handlers/index.js +225 -66
- package/lib/input-tracing/index.js +25 -2
- package/lib/input-tracing/install/child-process.js +28 -7
- package/lib/input-tracing/install/eval.js +60 -0
- package/lib/input-tracing/install/fs.js +21 -4
- package/lib/input-tracing/install/http.js +63 -0
- package/lib/input-tracing/install/mongodb.js +233 -0
- package/lib/input-tracing/install/mysql.js +21 -4
- package/lib/input-tracing/install/postgres.js +20 -4
- package/lib/input-tracing/install/sequelize.js +22 -5
- package/lib/input-tracing/install/sqlite3.js +21 -4
- package/lib/input-tracing/install/vm.js +132 -0
- package/lib/make-response-blocker.js +15 -0
- package/lib/make-source-context.js +22 -1
- package/lib/security-exception.js +15 -0
- package/lib/semantic-analysis/handlers.js +160 -0
- package/lib/semantic-analysis/index.js +38 -0
- package/lib/throw-security-exception.js +17 -6
- package/package.json +10 -12
- package/lib/cli-rewriter.js +0 -20
- package/lib/input-analysis/install/co-body.js +0 -51
- package/lib/input-analysis/install/cookie-parser.js +0 -48
- package/lib/input-analysis/install/formidable.js +0 -53
- package/lib/input-analysis/install/multer.js +0 -52
- package/lib/input-analysis/install/qs.js +0 -40
- package/lib/input-analysis/install/universal-cookie.js +0 -34
- package/lib/input-tracing/handlers/nosql-injection-mongo.js +0 -48
- package/lib/utils.js +0 -88
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
const { isSecurityException } = require('../../security-exception');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Function that exports an install method to patch Express framework with our instrumentation
|
|
23
|
+
* @param {Object} core - the core Contrast object in v5
|
|
24
|
+
* @return {Object} object with install method and the other relative functions exported for testing purposes
|
|
25
|
+
*/
|
|
26
|
+
module.exports = (core) => {
|
|
27
|
+
const {
|
|
28
|
+
depHooks,
|
|
29
|
+
patcher,
|
|
30
|
+
logger,
|
|
31
|
+
protect,
|
|
32
|
+
protect: { inputAnalysis },
|
|
33
|
+
} = core;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* registers a depHook for express module instrumentation
|
|
37
|
+
*/
|
|
38
|
+
function install() {
|
|
39
|
+
depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/middleware/query.js' }, (query) => patcher.patch(query, {
|
|
40
|
+
name: 'Express.query',
|
|
41
|
+
patchType,
|
|
42
|
+
post(data) {
|
|
43
|
+
data.result = patcher.patch(data.result, {
|
|
44
|
+
name: 'Express.query',
|
|
45
|
+
patchType,
|
|
46
|
+
pre(data) {
|
|
47
|
+
const [req, , origNext] = data.args;
|
|
48
|
+
|
|
49
|
+
function contrastNext(origErr) {
|
|
50
|
+
const sourceContext = protect.getSourceContext('Express.query');
|
|
51
|
+
let securityException;
|
|
52
|
+
|
|
53
|
+
// It is possible for the query to be already parsed by `qs`
|
|
54
|
+
// which means that we've already handled/analyzed it.
|
|
55
|
+
// So we check whether we already have the `parsedQuery` property in the context
|
|
56
|
+
if (sourceContext && req.query && Object.keys(req.query).length && (!('parsedQuery' in sourceContext))) {
|
|
57
|
+
sourceContext.parsedQuery = req.query;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
inputAnalysis.handleQueryParams(sourceContext, req.query);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
if (isSecurityException(err)) {
|
|
63
|
+
securityException = err;
|
|
64
|
+
} else {
|
|
65
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const error = securityException || origErr;
|
|
71
|
+
|
|
72
|
+
origNext(error);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
data.args[2] = contrastNext;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/layer.js' }, (Layer) => {
|
|
82
|
+
patcher.patch(Layer.prototype, 'handle_request', {
|
|
83
|
+
name: 'express.Layer.prototype.handle_request',
|
|
84
|
+
patchType,
|
|
85
|
+
pre(data) {
|
|
86
|
+
const { obj: { params } } = data;
|
|
87
|
+
const sourceContext = protect.getSourceContext('Express.Layer.handle_request');
|
|
88
|
+
|
|
89
|
+
if (sourceContext && params && Object.keys(params).length) {
|
|
90
|
+
sourceContext.parsedParams = params;
|
|
91
|
+
inputAnalysis.handleUrlParams(sourceContext, params);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const express4Instrumentation = inputAnalysis.express4Instrumentation = {
|
|
99
|
+
install
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return express4Instrumentation;
|
|
103
|
+
};
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
const { isSecurityException } = require('../../security-exception');
|
|
20
|
+
|
|
3
21
|
/**
|
|
4
22
|
* Function that exports an install method to patch Fastify framework with our instrumentation
|
|
5
23
|
* @param {Object} core - the core Contrast object in v5
|
|
@@ -10,7 +28,7 @@ module.exports = (core) => {
|
|
|
10
28
|
depHooks,
|
|
11
29
|
patcher,
|
|
12
30
|
logger,
|
|
13
|
-
|
|
31
|
+
protect,
|
|
14
32
|
protect: { inputAnalysis },
|
|
15
33
|
} = core;
|
|
16
34
|
|
|
@@ -29,7 +47,7 @@ module.exports = (core) => {
|
|
|
29
47
|
function patchFastify(fastify) {
|
|
30
48
|
return patcher.patch(fastify, {
|
|
31
49
|
name: 'fastify.build',
|
|
32
|
-
patchType
|
|
50
|
+
patchType,
|
|
33
51
|
post({ result: server }) {
|
|
34
52
|
server.addHook('preValidation', preValidationHook);
|
|
35
53
|
},
|
|
@@ -44,36 +62,45 @@ module.exports = (core) => {
|
|
|
44
62
|
* @param {Function} done callback to signal the hook is finished.
|
|
45
63
|
*/
|
|
46
64
|
function preValidationHook(request, reply, done) {
|
|
47
|
-
const sourceContext =
|
|
65
|
+
const sourceContext = protect.getSourceContext('Fastify.preValidationHook');
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
logger.debug('source context not available in fastify prevalidation hook');
|
|
51
|
-
} else {
|
|
52
|
-
if (request.params) {
|
|
53
|
-
sourceContext.parsedParams = request.params;
|
|
54
|
-
inputAnalysis.handleUrlParams(sourceContext, request.params);
|
|
55
|
-
}
|
|
56
|
-
if (request.cookies) {
|
|
57
|
-
sourceContext.parsedCookies = request.cookies;
|
|
58
|
-
inputAnalysis.handleCookies(sourceContext, request.cookies);
|
|
59
|
-
}
|
|
60
|
-
if (request.body) {
|
|
61
|
-
sourceContext.parsedBody = request.body;
|
|
62
|
-
inputAnalysis.handleParsedBody(sourceContext, request.body);
|
|
63
|
-
}
|
|
67
|
+
let securityException;
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
if (sourceContext) {
|
|
70
|
+
try {
|
|
71
|
+
if (request.params) {
|
|
72
|
+
sourceContext.parsedParams = request.params;
|
|
73
|
+
inputAnalysis.handleUrlParams(sourceContext, request.params);
|
|
74
|
+
}
|
|
75
|
+
if (request.cookies) {
|
|
76
|
+
sourceContext.parsedCookies = request.cookies;
|
|
77
|
+
inputAnalysis.handleCookies(sourceContext, request.cookies);
|
|
78
|
+
}
|
|
79
|
+
if (request.body) {
|
|
80
|
+
sourceContext.parsedBody = request.body;
|
|
81
|
+
inputAnalysis.handleParsedBody(sourceContext, request.body);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (request.query) {
|
|
85
|
+
sourceContext.parsedQuery = request.query;
|
|
86
|
+
inputAnalysis.handleQueryParams(sourceContext, request.query);
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
if (isSecurityException(err)) {
|
|
90
|
+
securityException = err;
|
|
91
|
+
} else {
|
|
92
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
93
|
+
}
|
|
68
94
|
}
|
|
69
95
|
}
|
|
70
|
-
|
|
96
|
+
|
|
97
|
+
done(securityException);
|
|
71
98
|
}
|
|
72
99
|
|
|
73
|
-
const
|
|
100
|
+
const fastify3Instrumentation = inputAnalysis.fastify3Instrumentation = {
|
|
74
101
|
preValidationHook,
|
|
75
102
|
install,
|
|
76
103
|
};
|
|
77
104
|
|
|
78
|
-
return
|
|
105
|
+
return fastify3Instrumentation;
|
|
79
106
|
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
patcher,
|
|
24
|
+
logger,
|
|
25
|
+
protect,
|
|
26
|
+
protect: { inputAnalysis },
|
|
27
|
+
} = core;
|
|
28
|
+
|
|
29
|
+
// Patch `formidable`
|
|
30
|
+
function install() {
|
|
31
|
+
depHooks.resolve({ name: 'formidable' }, (formidable) => {
|
|
32
|
+
formidable.IncomingForm.prototype.parse = patcher.patch(formidable.IncomingForm.prototype.parse, {
|
|
33
|
+
name: 'Formidable.IncomingForm.prototype.parse',
|
|
34
|
+
patchType,
|
|
35
|
+
pre(data) {
|
|
36
|
+
const origCb = data.args[1];
|
|
37
|
+
|
|
38
|
+
function hookedCb(...cbArgs) {
|
|
39
|
+
const sourceContext = protect.getSourceContext('formidable');
|
|
40
|
+
|
|
41
|
+
const [, fields, files] = cbArgs;
|
|
42
|
+
|
|
43
|
+
if (sourceContext) {
|
|
44
|
+
if (fields) {
|
|
45
|
+
sourceContext.parsedBody = fields;
|
|
46
|
+
inputAnalysis.handleParsedBody(sourceContext, fields);
|
|
47
|
+
}
|
|
48
|
+
if (files) {
|
|
49
|
+
logger.debug('Check for vulnerable filename upload nyi');
|
|
50
|
+
// TODO: NODE-2601
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (origCb && typeof origCb === 'function') {
|
|
55
|
+
origCb.apply(this, cbArgs);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
data.args[1] = hookedCb;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return formidable;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const formidable1Instrumentation = inputAnalysis.formidable1Instrumentation = {
|
|
68
|
+
install
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return formidable1Instrumentation;
|
|
72
|
+
};
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
3
18
|
const { Event } = require('@contrast/common');
|
|
@@ -18,7 +33,6 @@ class HttpInstrumentation {
|
|
|
18
33
|
this.config = core.config;
|
|
19
34
|
this.logger = logger.child({ name: 'contrast:protect:input-analysis' });
|
|
20
35
|
this.depHooks = core.depHooks;
|
|
21
|
-
this.messages = core.messages;
|
|
22
36
|
this.protect = core.protect;
|
|
23
37
|
this.makeSourceContext = this.protect.makeSourceContext;
|
|
24
38
|
this.maxBodySize = 16 * 1024 * 1024;
|
|
@@ -39,7 +53,9 @@ class HttpInstrumentation {
|
|
|
39
53
|
this.hookHttps();
|
|
40
54
|
}
|
|
41
55
|
|
|
42
|
-
uninstall() {
|
|
56
|
+
uninstall() {
|
|
57
|
+
return null; //NYI
|
|
58
|
+
}
|
|
43
59
|
|
|
44
60
|
/**
|
|
45
61
|
* Sets hooks to instrument `http.Server.prototype`.
|
|
@@ -149,6 +165,7 @@ class HttpInstrumentation {
|
|
|
149
165
|
const connectInputs = {
|
|
150
166
|
headers: HttpInstrumentation.removeCookies(reqData.headers),
|
|
151
167
|
uriPath: reqData.uriPath,
|
|
168
|
+
rawUrl: req.url,
|
|
152
169
|
// TODO AGENT-203 - need to handle method-tampering rule.
|
|
153
170
|
method: reqData.method,
|
|
154
171
|
};
|
|
@@ -157,9 +174,18 @@ class HttpInstrumentation {
|
|
|
157
174
|
if (reqData.standardUrlParsing) {
|
|
158
175
|
connectInputs.queries = reqData.queries;
|
|
159
176
|
}
|
|
177
|
+
if (inputAnalysis.virtualPatchesEvaluators?.length) {
|
|
178
|
+
store.protect.virtualPatchesEvaluators.push(...inputAnalysis.virtualPatchesEvaluators.map((e) => new Map(e)));
|
|
179
|
+
}
|
|
180
|
+
if (inputAnalysis.ipDenylist?.length) {
|
|
181
|
+
block = inputAnalysis.handleIpDenylist(store.protect, inputAnalysis.ipDenylist);
|
|
182
|
+
}
|
|
183
|
+
if (inputAnalysis.ipAllowlist?.length) {
|
|
184
|
+
const allowed = inputAnalysis.handleIpAllowlist(store.protect, inputAnalysis.ipAllowlist);
|
|
185
|
+
if (!block) Object.assign(store.protect, { allowed });
|
|
186
|
+
}
|
|
160
187
|
|
|
161
|
-
block = inputAnalysis.handleConnect(store.protect, connectInputs);
|
|
162
|
-
|
|
188
|
+
block = block || inputAnalysis.handleConnect(store.protect, connectInputs);
|
|
163
189
|
} catch (err) {
|
|
164
190
|
this.logger.error({ err }, 'Error during input analysis');
|
|
165
191
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
patcher,
|
|
24
|
+
protect,
|
|
25
|
+
protect: { inputAnalysis },
|
|
26
|
+
} = core;
|
|
27
|
+
|
|
28
|
+
// Patch `koa-body` package
|
|
29
|
+
function install() {
|
|
30
|
+
depHooks.resolve({ name: 'koa-body' }, (koaBody) => patcher.patch(koaBody, {
|
|
31
|
+
name: 'koa-body',
|
|
32
|
+
patchType,
|
|
33
|
+
post(data) {
|
|
34
|
+
data.result = patcher.patch(data.result, {
|
|
35
|
+
name: 'koa-body',
|
|
36
|
+
patchType,
|
|
37
|
+
pre(data) {
|
|
38
|
+
const [ctx, origNext] = data.args;
|
|
39
|
+
|
|
40
|
+
async function contrastNext(origErr) {
|
|
41
|
+
const sourceContext = protect.getSourceContext('koa-body');
|
|
42
|
+
|
|
43
|
+
if (sourceContext && ctx.request.body && Object.keys(ctx.request.body).length) {
|
|
44
|
+
sourceContext.parsedBody = ctx.request.body;
|
|
45
|
+
inputAnalysis.handleParsedBody(sourceContext, ctx.request.body);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await origNext(origErr);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
data.args[1] = contrastNext;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const koaBody5Instrumentation = inputAnalysis.koaBody5Instrumentation = {
|
|
59
|
+
install
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return koaBody5Instrumentation;
|
|
63
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
patcher,
|
|
24
|
+
protect,
|
|
25
|
+
protect: { inputAnalysis },
|
|
26
|
+
} = core;
|
|
27
|
+
|
|
28
|
+
// Patch `koa-bodyparser` package
|
|
29
|
+
function install() {
|
|
30
|
+
depHooks.resolve({ name: 'koa-bodyparser' }, (koaBodyparser) => patcher.patch(koaBodyparser, {
|
|
31
|
+
name: 'koa-bodyparser',
|
|
32
|
+
patchType,
|
|
33
|
+
post(data) {
|
|
34
|
+
data.result = patcher.patch(data.result, {
|
|
35
|
+
name: 'koa-bodyparser',
|
|
36
|
+
patchType,
|
|
37
|
+
pre(data) {
|
|
38
|
+
const [ctx, origNext] = data.args;
|
|
39
|
+
|
|
40
|
+
async function contrastNext(origErr) {
|
|
41
|
+
const sourceContext = protect.getSourceContext('koa-bodyparser');
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if (sourceContext && ctx.request.body && Object.keys(ctx.request.body).length) {
|
|
45
|
+
sourceContext.parsedBody = ctx.request.body;
|
|
46
|
+
inputAnalysis.handleParsedBody(sourceContext, ctx.request.body);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await origNext(origErr);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
data.args[1] = contrastNext;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const koaBodyparser4Instrumentation = inputAnalysis.koaBodyparser4Instrumentation = {
|
|
60
|
+
install
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return koaBodyparser4Instrumentation;
|
|
64
|
+
};
|
|
@@ -1,7 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
|
|
3
20
|
/**
|
|
4
|
-
* Function that exports an install method to patch
|
|
21
|
+
* Function that exports an install method to patch Koa framework with our instrumentation
|
|
5
22
|
* @param {Object} core - the core Contrast object in v5
|
|
6
23
|
* @return {Object} object with install method and the other relative functions exported for testing purposes
|
|
7
24
|
*/
|
|
@@ -9,8 +26,7 @@ module.exports = (core) => {
|
|
|
9
26
|
const {
|
|
10
27
|
depHooks,
|
|
11
28
|
patcher,
|
|
12
|
-
|
|
13
|
-
scopes: { sources },
|
|
29
|
+
protect,
|
|
14
30
|
protect: { inputAnalysis },
|
|
15
31
|
} = core;
|
|
16
32
|
|
|
@@ -19,25 +35,14 @@ module.exports = (core) => {
|
|
|
19
35
|
*/
|
|
20
36
|
function install() {
|
|
21
37
|
depHooks.resolve({ name: 'koa', version: '>=2.3.0' }, (Koa) => {
|
|
22
|
-
const coBodyPatch = require('./co-body.js')(core);
|
|
23
|
-
const multerPatch = require('./multer')(core);
|
|
24
|
-
const formidablePatch = require('./formidable')(core);
|
|
25
|
-
const qsPatch = require('./qs')(core);
|
|
26
|
-
const cookieParserPatch = require('./cookie-parser')(core);
|
|
27
|
-
const universalCookiePatch = require('./universal-cookie')(core);
|
|
28
|
-
|
|
29
|
-
|
|
30
38
|
function contrastStartMiddleware(ctx, next) {
|
|
31
39
|
if (ctx.query && Object.keys(ctx.query).length) {
|
|
32
|
-
const sourceContext =
|
|
40
|
+
const sourceContext = protect.getSourceContext('Koa startMiddleware');
|
|
33
41
|
|
|
34
|
-
if (!sourceContext) {
|
|
35
|
-
logger.debug('source context not available in `qs` hook');
|
|
36
|
-
} else if (!('parsedQuery' in sourceContext)) {
|
|
42
|
+
if (sourceContext && !('parsedQuery' in sourceContext)) {
|
|
37
43
|
sourceContext.parsedQuery = ctx.query;
|
|
38
44
|
inputAnalysis.handleQueryParams(sourceContext, ctx.query);
|
|
39
45
|
}
|
|
40
|
-
|
|
41
46
|
}
|
|
42
47
|
return next();
|
|
43
48
|
}
|
|
@@ -47,7 +52,7 @@ module.exports = (core) => {
|
|
|
47
52
|
|
|
48
53
|
patcher.patch(Koa.prototype, 'use', {
|
|
49
54
|
name: 'Koa.Application',
|
|
50
|
-
patchType
|
|
55
|
+
patchType,
|
|
51
56
|
pre({ obj: app }) {
|
|
52
57
|
// if not already inserted, insert the initial middleware.
|
|
53
58
|
if (
|
|
@@ -66,17 +71,13 @@ module.exports = (core) => {
|
|
|
66
71
|
(layer) => {
|
|
67
72
|
layer.prototype = patcher.patch(layer.prototype, 'params', {
|
|
68
73
|
name: `[${router}].layer.prototype`,
|
|
69
|
-
patchType
|
|
74
|
+
patchType,
|
|
70
75
|
post({ result }) {
|
|
71
|
-
const sourceContext =
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (Object.keys(result).length) {
|
|
77
|
-
sourceContext.parsedParams = result;
|
|
78
|
-
inputAnalysis.handleUrlParams(sourceContext, result);
|
|
79
|
-
}
|
|
76
|
+
const sourceContext = protect.getSourceContext(`[${router}].layer`);
|
|
77
|
+
|
|
78
|
+
if (sourceContext && Object.keys(result).length) {
|
|
79
|
+
sourceContext.parsedParams = result;
|
|
80
|
+
inputAnalysis.handleUrlParams(sourceContext, result);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
});
|
|
@@ -90,27 +91,23 @@ module.exports = (core) => {
|
|
|
90
91
|
const { default: cookieParser } = koaCookie;
|
|
91
92
|
koaCookie.default = patcher.patch(cookieParser, {
|
|
92
93
|
name: 'koa-cookie',
|
|
93
|
-
patchType
|
|
94
|
+
patchType,
|
|
94
95
|
post(data) {
|
|
95
96
|
data.result = patcher.patch(data.result, {
|
|
96
97
|
name: 'koa-cookie',
|
|
97
|
-
patchType
|
|
98
|
+
patchType,
|
|
98
99
|
pre(data) {
|
|
99
100
|
const [ctx, origNext] = data.args;
|
|
100
101
|
|
|
101
|
-
async function contrastNext() {
|
|
102
|
-
const sourceContext =
|
|
102
|
+
async function contrastNext(origErr) {
|
|
103
|
+
const sourceContext = protect.getSourceContext('koa-cookie');
|
|
103
104
|
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (ctx.cookie) {
|
|
108
|
-
sourceContext.parsedCookies = ctx.cookie;
|
|
109
|
-
inputAnalysis.handleCookies(sourceContext, ctx.cookie);
|
|
110
|
-
}
|
|
105
|
+
if (sourceContext && ctx.cookie) {
|
|
106
|
+
sourceContext.parsedCookies = ctx.cookie;
|
|
107
|
+
inputAnalysis.handleCookies(sourceContext, ctx.cookie);
|
|
111
108
|
}
|
|
112
109
|
|
|
113
|
-
await origNext();
|
|
110
|
+
await origNext(origErr);
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
data.args[1] = contrastNext;
|
|
@@ -119,19 +116,12 @@ module.exports = (core) => {
|
|
|
119
116
|
}
|
|
120
117
|
});
|
|
121
118
|
});
|
|
122
|
-
|
|
123
|
-
coBodyPatch.install();
|
|
124
|
-
multerPatch.install();
|
|
125
|
-
formidablePatch.install();
|
|
126
|
-
qsPatch.install();
|
|
127
|
-
cookieParserPatch.install();
|
|
128
|
-
universalCookiePatch.install();
|
|
129
119
|
});
|
|
130
120
|
}
|
|
131
121
|
|
|
132
|
-
const
|
|
122
|
+
const koa2Instrumentation = inputAnalysis.koa2Instrumentation = {
|
|
133
123
|
install
|
|
134
124
|
};
|
|
135
125
|
|
|
136
|
-
return
|
|
126
|
+
return koa2Instrumentation;
|
|
137
127
|
};
|