@contrast/protect 1.2.0 → 1.3.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/cli-rewriter.js +15 -0
- package/lib/error-handlers/constants.js +15 -0
- package/lib/error-handlers/index.js +17 -0
- package/lib/error-handlers/install/express4.js +90 -0
- package/lib/error-handlers/install/fastify3.js +15 -0
- package/lib/error-handlers/install/koa2.js +15 -0
- package/lib/esm-loader.mjs +15 -0
- package/lib/index.d.ts +145 -19
- package/lib/index.js +15 -0
- package/lib/input-analysis/constants.js +20 -0
- package/lib/input-analysis/handlers.js +33 -10
- package/lib/input-analysis/index.js +36 -3
- package/lib/input-analysis/install/body-parser1.js +130 -0
- package/lib/input-analysis/install/cookie-parser1.js +82 -0
- package/lib/input-analysis/install/express4.js +108 -0
- package/lib/input-analysis/install/fastify3.js +47 -19
- package/lib/input-analysis/install/formidable1.js +73 -0
- package/lib/input-analysis/install/http.js +18 -1
- package/lib/input-analysis/install/koa-body5.js +68 -0
- package/lib/input-analysis/install/koa-bodyparser4.js +68 -0
- package/lib/input-analysis/install/koa2.js +26 -25
- package/lib/input-analysis/install/multer1.js +89 -0
- package/lib/input-analysis/install/qs6.js +61 -0
- package/lib/input-analysis/install/universal-cookie4.js +56 -0
- package/lib/input-tracing/constants.js +15 -0
- package/lib/input-tracing/handlers/index.js +154 -59
- package/lib/input-tracing/index.js +17 -0
- package/lib/input-tracing/install/child-process.js +16 -1
- package/lib/input-tracing/install/fs.js +17 -2
- package/lib/input-tracing/install/mongodb.js +233 -0
- package/lib/input-tracing/install/mysql.js +18 -2
- package/lib/input-tracing/install/postgres.js +15 -0
- package/lib/input-tracing/install/sequelize.js +15 -0
- package/lib/input-tracing/install/sqlite3.js +15 -0
- package/lib/make-response-blocker.js +15 -0
- package/lib/make-source-context.js +18 -0
- package/lib/security-exception.js +15 -0
- package/lib/throw-security-exception.js +17 -6
- package/lib/utils.js +14 -18
- package/package.json +7 -7
- 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
|
@@ -0,0 +1,130 @@
|
|
|
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 { isSecurityException } = require('../../security-exception');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
logger,
|
|
24
|
+
scopes: { sources },
|
|
25
|
+
protect: { inputAnalysis },
|
|
26
|
+
} = core;
|
|
27
|
+
|
|
28
|
+
function contrastNext(req, origNext, fnName) {
|
|
29
|
+
return function next(origErr) {
|
|
30
|
+
const sourceContext = sources.getStore()?.protect;
|
|
31
|
+
let securityException;
|
|
32
|
+
|
|
33
|
+
if (!sourceContext) {
|
|
34
|
+
logger.debug(`source context not available in \`body-parser\`'s \`${fnName}\` hook`);
|
|
35
|
+
} else {
|
|
36
|
+
if (req.body && Object.keys(req.body).length) {
|
|
37
|
+
sourceContext.parsedBody = req.body;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
inputAnalysis.handleParsedBody(sourceContext, req.body);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
if (isSecurityException(err)) {
|
|
43
|
+
securityException = err;
|
|
44
|
+
} else {
|
|
45
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const error = securityException || origErr;
|
|
51
|
+
|
|
52
|
+
origNext(error);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Patch body parser - `body-parser` used by `express` framework
|
|
57
|
+
function install() {
|
|
58
|
+
depHooks.resolve({ name: 'body-parser' }, (bodyParser) => {
|
|
59
|
+
const origBodyParser = bodyParser;
|
|
60
|
+
|
|
61
|
+
const { json: origJson, raw: origRaw, text: origText, urlencoded: origUrlencoded } = bodyParser;
|
|
62
|
+
const fnArr = [
|
|
63
|
+
{
|
|
64
|
+
key: 'json',
|
|
65
|
+
original: origJson,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: 'raw',
|
|
69
|
+
original: origRaw,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: 'text',
|
|
73
|
+
original: origText,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: 'urlencoded',
|
|
77
|
+
original: origUrlencoded,
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
bodyParser = function bodyParser(...args) {
|
|
82
|
+
const parser = origBodyParser(...args);
|
|
83
|
+
const hookedParser = function(req, res, next) {
|
|
84
|
+
parser(req, res, contrastNext(req, next, 'bodyParser'));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
Object.defineProperty(hookedParser, 'name', {
|
|
88
|
+
value: 'bodyParser'
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return hookedParser;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
fnArr.forEach((fn) => {
|
|
95
|
+
const fnName = `bodyParser.${fn.key}`;
|
|
96
|
+
function contrastHooked(...args) {
|
|
97
|
+
const parser = fn.original(...args);
|
|
98
|
+
const hookedParser = function (req, res, next) {
|
|
99
|
+
if (!req.body) {
|
|
100
|
+
parser(req, res, next);
|
|
101
|
+
} else {
|
|
102
|
+
parser(req, res, contrastNext(req, next, fnName));
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
Object.defineProperty(hookedParser, 'name', {
|
|
107
|
+
value: `${fn.key}Parser`
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return hookedParser;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
Object.defineProperty(bodyParser, fn.key, {
|
|
114
|
+
configurable: true,
|
|
115
|
+
enumerable: true,
|
|
116
|
+
get: () => contrastHooked,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return bodyParser;
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const bodyParser1Instrumentation = inputAnalysis.bodyParser1Instrumentation = {
|
|
126
|
+
install
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return bodyParser1Instrumentation;
|
|
130
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
module.exports = (core) => {
|
|
22
|
+
const {
|
|
23
|
+
depHooks,
|
|
24
|
+
patcher,
|
|
25
|
+
logger,
|
|
26
|
+
scopes: { sources },
|
|
27
|
+
protect: { inputAnalysis },
|
|
28
|
+
} = core;
|
|
29
|
+
|
|
30
|
+
// Patch `cookie-parser` package
|
|
31
|
+
function install() {
|
|
32
|
+
depHooks.resolve({ name: 'cookie-parser' }, (cookieParser) => patcher.patch(cookieParser, {
|
|
33
|
+
name: 'cookie-parser',
|
|
34
|
+
patchType,
|
|
35
|
+
post(data) {
|
|
36
|
+
data.result = patcher.patch(data.result, {
|
|
37
|
+
name: 'cookie-parser',
|
|
38
|
+
patchType,
|
|
39
|
+
pre(data) {
|
|
40
|
+
const [req, , origNext] = data.args;
|
|
41
|
+
|
|
42
|
+
function contrastNext(origErr) {
|
|
43
|
+
const sourceContext = sources.getStore()?.protect;
|
|
44
|
+
let securityException;
|
|
45
|
+
|
|
46
|
+
if (!sourceContext) {
|
|
47
|
+
logger.debug('source context not available in `cookie-parser` hook');
|
|
48
|
+
} else {
|
|
49
|
+
if ((req.cookies && Object.keys(req.cookies).length) || (req.signedCookies && Object.keys(req.signedCookies).length)) {
|
|
50
|
+
sourceContext.parsedCookies = { ...req.cookies, ...req.signedCookies };
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
inputAnalysis.handleCookies(sourceContext, sourceContext.parsedCookies);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
if (isSecurityException(err)) {
|
|
56
|
+
securityException = err;
|
|
57
|
+
} else {
|
|
58
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const error = securityException || origErr;
|
|
65
|
+
|
|
66
|
+
origNext(error);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
data.args[2] = contrastNext;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const cookieParser1Instrumentation = inputAnalysis.cookieParser1Instrumentation = {
|
|
78
|
+
install
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return cookieParser1Instrumentation;
|
|
82
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
scopes: { sources },
|
|
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 = sources.getStore()?.protect;
|
|
51
|
+
let securityException;
|
|
52
|
+
|
|
53
|
+
if (!sourceContext) {
|
|
54
|
+
logger.debug('source context not available in `Express.query` hook');
|
|
55
|
+
|
|
56
|
+
// It is possible for the query to be already parsed by `qs`
|
|
57
|
+
// which means that we've already handled/analyzed it.
|
|
58
|
+
// So we check whether we already have the `parsedQuery` property in the context
|
|
59
|
+
} else if (req.query && Object.keys(req.query).length && (!('parsedQuery' in sourceContext))) {
|
|
60
|
+
sourceContext.parsedQuery = req.query;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
inputAnalysis.handleQueryParams(sourceContext, req.query);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (isSecurityException(err)) {
|
|
66
|
+
securityException = err;
|
|
67
|
+
} else {
|
|
68
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const error = securityException || origErr;
|
|
74
|
+
|
|
75
|
+
origNext(error);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
data.args[2] = contrastNext;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/layer.js' }, (Layer) => {
|
|
85
|
+
patcher.patch(Layer.prototype, 'handle_request', {
|
|
86
|
+
name: 'express.Layer.prototype.handle_request',
|
|
87
|
+
patchType,
|
|
88
|
+
pre(data) {
|
|
89
|
+
const { obj: { params } } = data;
|
|
90
|
+
const sourceContext = sources.getStore()?.protect;
|
|
91
|
+
|
|
92
|
+
if (!sourceContext) {
|
|
93
|
+
logger.debug('source context not available in `express.Layer.prototype.handle_request` hook');
|
|
94
|
+
} else if (params && Object.keys(params).length) {
|
|
95
|
+
sourceContext.parsedParams = params;
|
|
96
|
+
inputAnalysis.handleUrlParams(sourceContext, params);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const express4Instrumentation = inputAnalysis.express4Instrumentation = {
|
|
104
|
+
install
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return express4Instrumentation;
|
|
108
|
+
};
|
|
@@ -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
|
|
@@ -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
|
},
|
|
@@ -45,35 +63,45 @@ module.exports = (core) => {
|
|
|
45
63
|
*/
|
|
46
64
|
function preValidationHook(request, reply, done) {
|
|
47
65
|
const sourceContext = sources.getStore()?.protect;
|
|
66
|
+
let securityException;
|
|
48
67
|
|
|
49
68
|
if (!sourceContext) {
|
|
50
69
|
logger.debug('source context not available in fastify prevalidation hook');
|
|
51
70
|
} else {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
try {
|
|
72
|
+
if (request.params) {
|
|
73
|
+
sourceContext.parsedParams = request.params;
|
|
74
|
+
inputAnalysis.handleUrlParams(sourceContext, request.params);
|
|
75
|
+
}
|
|
76
|
+
if (request.cookies) {
|
|
77
|
+
sourceContext.parsedCookies = request.cookies;
|
|
78
|
+
inputAnalysis.handleCookies(sourceContext, request.cookies);
|
|
79
|
+
}
|
|
80
|
+
if (request.body) {
|
|
81
|
+
sourceContext.parsedBody = request.body;
|
|
82
|
+
inputAnalysis.handleParsedBody(sourceContext, request.body);
|
|
83
|
+
}
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
85
|
+
if (request.query) {
|
|
86
|
+
sourceContext.parsedQuery = request.query;
|
|
87
|
+
inputAnalysis.handleQueryParams(sourceContext, request.query);
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
if (isSecurityException(err)) {
|
|
91
|
+
securityException = err;
|
|
92
|
+
} else {
|
|
93
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
94
|
+
}
|
|
68
95
|
}
|
|
69
96
|
}
|
|
70
|
-
|
|
97
|
+
|
|
98
|
+
done(securityException);
|
|
71
99
|
}
|
|
72
100
|
|
|
73
|
-
const
|
|
101
|
+
const fastify3Instrumentation = inputAnalysis.fastify3Instrumentation = {
|
|
74
102
|
preValidationHook,
|
|
75
103
|
install,
|
|
76
104
|
};
|
|
77
105
|
|
|
78
|
-
return
|
|
106
|
+
return fastify3Instrumentation;
|
|
79
107
|
};
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
scopes: { sources },
|
|
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 = sources.getStore()?.protect;
|
|
40
|
+
const [, fields, files] = cbArgs;
|
|
41
|
+
|
|
42
|
+
if (!sourceContext) {
|
|
43
|
+
logger.debug('source context not available in `formidable` hook');
|
|
44
|
+
} else {
|
|
45
|
+
if (fields) {
|
|
46
|
+
sourceContext.parsedBody = fields;
|
|
47
|
+
inputAnalysis.handleParsedBody(sourceContext, fields);
|
|
48
|
+
}
|
|
49
|
+
if (files) {
|
|
50
|
+
logger.debug('Check for vulnerable filename upload nyi');
|
|
51
|
+
// TODO: NODE-2601
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (origCb && typeof origCb === 'function') {
|
|
56
|
+
origCb.apply(this, cbArgs);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
data.args[1] = hookedCb;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return formidable;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const formidable1Instrumentation = inputAnalysis.formidable1Instrumentation = {
|
|
69
|
+
install
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return formidable1Instrumentation;
|
|
73
|
+
};
|
|
@@ -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');
|
|
@@ -39,7 +54,9 @@ class HttpInstrumentation {
|
|
|
39
54
|
this.hookHttps();
|
|
40
55
|
}
|
|
41
56
|
|
|
42
|
-
uninstall() {
|
|
57
|
+
uninstall() {
|
|
58
|
+
return null; //NYI
|
|
59
|
+
}
|
|
43
60
|
|
|
44
61
|
/**
|
|
45
62
|
* Sets hooks to instrument `http.Server.prototype`.
|
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
scopes: { sources },
|
|
26
|
+
protect: { inputAnalysis },
|
|
27
|
+
} = core;
|
|
28
|
+
|
|
29
|
+
// Patch `koa-body` package
|
|
30
|
+
function install() {
|
|
31
|
+
depHooks.resolve({ name: 'koa-body' }, (koaBody) => patcher.patch(koaBody, {
|
|
32
|
+
name: 'koa-body',
|
|
33
|
+
patchType,
|
|
34
|
+
post(data) {
|
|
35
|
+
data.result = patcher.patch(data.result, {
|
|
36
|
+
name: 'koa-body',
|
|
37
|
+
patchType,
|
|
38
|
+
pre(data) {
|
|
39
|
+
const [ctx, origNext] = data.args;
|
|
40
|
+
|
|
41
|
+
async function contrastNext(origErr) {
|
|
42
|
+
const sourceContext = sources.getStore()?.protect;
|
|
43
|
+
|
|
44
|
+
if (!sourceContext) {
|
|
45
|
+
logger.debug('source context not available in `koa-body` hook');
|
|
46
|
+
} else {
|
|
47
|
+
if (ctx.request.body && Object.keys(ctx.request.body).length) {
|
|
48
|
+
sourceContext.parsedBody = ctx.request.body;
|
|
49
|
+
inputAnalysis.handleParsedBody(sourceContext, ctx.request.body);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await origNext(origErr);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
data.args[1] = contrastNext;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const koaBody5Instrumentation = inputAnalysis.koaBody5Instrumentation = {
|
|
64
|
+
install
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return koaBody5Instrumentation;
|
|
68
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
scopes: { sources },
|
|
26
|
+
protect: { inputAnalysis },
|
|
27
|
+
} = core;
|
|
28
|
+
|
|
29
|
+
// Patch `koa-bodyparser` package
|
|
30
|
+
function install() {
|
|
31
|
+
depHooks.resolve({ name: 'koa-bodyparser' }, (koaBodyparser) => patcher.patch(koaBodyparser, {
|
|
32
|
+
name: 'koa-bodyparser',
|
|
33
|
+
patchType,
|
|
34
|
+
post(data) {
|
|
35
|
+
data.result = patcher.patch(data.result, {
|
|
36
|
+
name: 'koa-bodyparser',
|
|
37
|
+
patchType,
|
|
38
|
+
pre(data) {
|
|
39
|
+
const [ctx, origNext] = data.args;
|
|
40
|
+
|
|
41
|
+
async function contrastNext(origErr) {
|
|
42
|
+
const sourceContext = sources.getStore()?.protect;
|
|
43
|
+
|
|
44
|
+
if (!sourceContext) {
|
|
45
|
+
logger.debug('source context not available in `koa-bodyparser` hook');
|
|
46
|
+
} else {
|
|
47
|
+
if (ctx.request.body && Object.keys(ctx.request.body).length) {
|
|
48
|
+
sourceContext.parsedBody = ctx.request.body;
|
|
49
|
+
inputAnalysis.handleParsedBody(sourceContext, ctx.request.body);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await origNext(origErr);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
data.args[1] = contrastNext;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const koaBodyparser4Instrumentation = inputAnalysis.koaBodyparser4Instrumentation = {
|
|
64
|
+
install
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return koaBodyparser4Instrumentation;
|
|
68
|
+
};
|