@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
|
@@ -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
|
*/
|
|
@@ -19,14 +36,6 @@ module.exports = (core) => {
|
|
|
19
36
|
*/
|
|
20
37
|
function install() {
|
|
21
38
|
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
39
|
function contrastStartMiddleware(ctx, next) {
|
|
31
40
|
if (ctx.query && Object.keys(ctx.query).length) {
|
|
32
41
|
const sourceContext = sources.getStore()?.protect;
|
|
@@ -37,7 +46,6 @@ module.exports = (core) => {
|
|
|
37
46
|
sourceContext.parsedQuery = ctx.query;
|
|
38
47
|
inputAnalysis.handleQueryParams(sourceContext, ctx.query);
|
|
39
48
|
}
|
|
40
|
-
|
|
41
49
|
}
|
|
42
50
|
return next();
|
|
43
51
|
}
|
|
@@ -47,7 +55,7 @@ module.exports = (core) => {
|
|
|
47
55
|
|
|
48
56
|
patcher.patch(Koa.prototype, 'use', {
|
|
49
57
|
name: 'Koa.Application',
|
|
50
|
-
patchType
|
|
58
|
+
patchType,
|
|
51
59
|
pre({ obj: app }) {
|
|
52
60
|
// if not already inserted, insert the initial middleware.
|
|
53
61
|
if (
|
|
@@ -66,7 +74,7 @@ module.exports = (core) => {
|
|
|
66
74
|
(layer) => {
|
|
67
75
|
layer.prototype = patcher.patch(layer.prototype, 'params', {
|
|
68
76
|
name: `[${router}].layer.prototype`,
|
|
69
|
-
patchType
|
|
77
|
+
patchType,
|
|
70
78
|
post({ result }) {
|
|
71
79
|
const sourceContext = sources.getStore()?.protect;
|
|
72
80
|
|
|
@@ -90,15 +98,15 @@ module.exports = (core) => {
|
|
|
90
98
|
const { default: cookieParser } = koaCookie;
|
|
91
99
|
koaCookie.default = patcher.patch(cookieParser, {
|
|
92
100
|
name: 'koa-cookie',
|
|
93
|
-
patchType
|
|
101
|
+
patchType,
|
|
94
102
|
post(data) {
|
|
95
103
|
data.result = patcher.patch(data.result, {
|
|
96
104
|
name: 'koa-cookie',
|
|
97
|
-
patchType
|
|
105
|
+
patchType,
|
|
98
106
|
pre(data) {
|
|
99
107
|
const [ctx, origNext] = data.args;
|
|
100
108
|
|
|
101
|
-
async function contrastNext() {
|
|
109
|
+
async function contrastNext(origErr) {
|
|
102
110
|
const sourceContext = sources.getStore()?.protect;
|
|
103
111
|
|
|
104
112
|
if (!sourceContext) {
|
|
@@ -110,7 +118,7 @@ module.exports = (core) => {
|
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
|
|
113
|
-
await origNext();
|
|
121
|
+
await origNext(origErr);
|
|
114
122
|
}
|
|
115
123
|
|
|
116
124
|
data.args[1] = contrastNext;
|
|
@@ -119,19 +127,12 @@ module.exports = (core) => {
|
|
|
119
127
|
}
|
|
120
128
|
});
|
|
121
129
|
});
|
|
122
|
-
|
|
123
|
-
coBodyPatch.install();
|
|
124
|
-
multerPatch.install();
|
|
125
|
-
formidablePatch.install();
|
|
126
|
-
qsPatch.install();
|
|
127
|
-
cookieParserPatch.install();
|
|
128
|
-
universalCookiePatch.install();
|
|
129
130
|
});
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
const
|
|
133
|
+
const koa2Instrumentation = inputAnalysis.koa2Instrumentation = {
|
|
133
134
|
install
|
|
134
135
|
};
|
|
135
136
|
|
|
136
|
-
return
|
|
137
|
+
return koa2Instrumentation;
|
|
137
138
|
};
|
|
@@ -0,0 +1,89 @@
|
|
|
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, wrap },
|
|
27
|
+
protect: { inputAnalysis },
|
|
28
|
+
} = core;
|
|
29
|
+
|
|
30
|
+
// Patch `multer`
|
|
31
|
+
function install() {
|
|
32
|
+
depHooks.resolve({ name: 'multer', file: 'lib/make-middleware.js' }, (multerMakeMiddleware) => patcher.patch(multerMakeMiddleware, {
|
|
33
|
+
name: 'multer.make-middleware',
|
|
34
|
+
patchType,
|
|
35
|
+
post(data) {
|
|
36
|
+
data.result = patcher.patch(data.result, {
|
|
37
|
+
name: 'multerMiddleware',
|
|
38
|
+
patchType,
|
|
39
|
+
pre(data) {
|
|
40
|
+
const [req, , origNext] = data.args;
|
|
41
|
+
|
|
42
|
+
// We are getting the sourceContext here because in the time of calling
|
|
43
|
+
// the contrastNext() method the context is lost
|
|
44
|
+
const sourceContext = sources.getStore()?.protect;
|
|
45
|
+
if (!sourceContext) {
|
|
46
|
+
logger.debug('source context not available in `multer` hook');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function contrastNext(origErr) {
|
|
51
|
+
let securityException;
|
|
52
|
+
|
|
53
|
+
if (req.body) {
|
|
54
|
+
sourceContext.parsedBody = req.body;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
inputAnalysis.handleParsedBody(sourceContext, req.body);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
if (isSecurityException(err)) {
|
|
60
|
+
securityException = err;
|
|
61
|
+
} else {
|
|
62
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (req.file || req.files) {
|
|
68
|
+
logger.debug('Check for vulnerable filename upload nyi');
|
|
69
|
+
// TODO: NODE-2601
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const error = securityException || origErr;
|
|
73
|
+
|
|
74
|
+
origNext(error);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
data.args[2] = wrap(contrastNext);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const multer1Instrumentation = inputAnalysis.multer1Instrumentation = {
|
|
85
|
+
install
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return multer1Instrumentation;
|
|
89
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
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 `qs`
|
|
30
|
+
function install() {
|
|
31
|
+
depHooks.resolve({ name: 'qs' },
|
|
32
|
+
(qs) => patcher.patch(qs, 'parse', {
|
|
33
|
+
name: 'qs',
|
|
34
|
+
patchType,
|
|
35
|
+
post({ args, result }) {
|
|
36
|
+
if (result && Object.keys(result).length) {
|
|
37
|
+
const sourceContext = sources.getStore()?.protect;
|
|
38
|
+
|
|
39
|
+
if (!sourceContext) {
|
|
40
|
+
logger.debug('source context not available in `qs` hook');
|
|
41
|
+
|
|
42
|
+
// We need to run analysis for the `qs` result only when it's used as a query parser.
|
|
43
|
+
// `qs` is used also for parsing bodies, but these cases we handle individually with
|
|
44
|
+
// the respective library that's using it (e.g. `formidable`, `co-body`) because in
|
|
45
|
+
// some cases its use is optional and we cannot rely on it.
|
|
46
|
+
} else if (sourceContext.reqData?.queries === args[0]) {
|
|
47
|
+
sourceContext.parsedQuery = result;
|
|
48
|
+
inputAnalysis.handleQueryParams(sourceContext, result);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const qs6Instrumentation = inputAnalysis.qs6Instrumentation = {
|
|
57
|
+
install
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return qs6Instrumentation;
|
|
61
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
|
|
30
|
+
// Patch `universal-cookie` package
|
|
31
|
+
function install() {
|
|
32
|
+
depHooks.resolve({ name: 'universal-cookie', file: 'cjs/utils.js' }, (uCookieUtils) => patcher.patch(uCookieUtils, 'parseCookies', {
|
|
33
|
+
name: 'universal-cookie.utils',
|
|
34
|
+
patchType,
|
|
35
|
+
post({ result }) {
|
|
36
|
+
if (result && Object.keys(result).length) {
|
|
37
|
+
const sourceContext = sources.getStore()?.protect;
|
|
38
|
+
|
|
39
|
+
if (!sourceContext) {
|
|
40
|
+
logger.debug('source context not available in `universal-cookie` hook');
|
|
41
|
+
} else {
|
|
42
|
+
sourceContext.parsedCookies = result;
|
|
43
|
+
inputAnalysis.handleCookies(sourceContext, result);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const universalCookie4Instrumentation = inputAnalysis.universalCookie4Instrumentation = {
|
|
52
|
+
install
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return universalCookie4Instrumentation;
|
|
56
|
+
};
|
|
@@ -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
|
module.exports = {
|
|
@@ -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 util = require('util');
|
|
19
|
+
const { simpleTraverse } = require('../../utils');
|
|
20
|
+
|
|
3
21
|
module.exports = function(core) {
|
|
4
22
|
const { protect: { agentLib, inputTracing, throwSecurityException } } = core;
|
|
5
23
|
|
|
@@ -9,87 +27,90 @@ module.exports = function(core) {
|
|
|
9
27
|
* @param {object} context async storage data for protect
|
|
10
28
|
* @returns {AnalysisResult[]}
|
|
11
29
|
*/
|
|
12
|
-
|
|
30
|
+
function getResultsByRuleId(ruleId, context) {
|
|
31
|
+
if (context.rules.agentLibRules[ruleId].mode === 'off') {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
13
34
|
return context.findings.resultsMap[ruleId];
|
|
14
|
-
}
|
|
35
|
+
}
|
|
15
36
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* with common setup and post-processing code.
|
|
19
|
-
*/
|
|
20
|
-
inputTracing.handlerFactory = function(ruleId, analysisFn) {
|
|
21
|
-
/**
|
|
22
|
-
* This is the common API for INPUT TRACING instrumentation.
|
|
23
|
-
* @param {object} sourceContext
|
|
24
|
-
* @param {object} sinkContext
|
|
25
|
-
*/
|
|
26
|
-
return function(sourceContext, sinkContext) {
|
|
27
|
-
if (sourceContext.rules.agentLibRules[ruleId].mode === 'off') {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
37
|
+
function handleFindings(sourceContext, sinkContext, ruleId, result, findings) {
|
|
38
|
+
result.details.push({ sinkContext, findings });
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
if (!results) return;
|
|
40
|
+
const { mode } = sourceContext.rules.agentLibRules[ruleId];
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
if (['block', 'block_at_perimeter'].includes(mode)) {
|
|
43
|
+
result.blocked = true;
|
|
44
|
+
const blockInfo = [mode, ruleId];
|
|
45
|
+
sourceContext.findings.securityException = blockInfo;
|
|
46
|
+
throwSecurityException(sourceContext);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
36
49
|
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
inputTracing.handlePathTraversal = function(sourceContext, sinkContext) {
|
|
51
|
+
const ruleId = 'path-traversal';
|
|
52
|
+
const results = getResultsByRuleId(ruleId, sourceContext);
|
|
39
53
|
|
|
40
|
-
|
|
41
|
-
result.blocked = true;
|
|
42
|
-
const blockInfo = ['block', ruleId];
|
|
43
|
-
sourceContext.findings.securityException = blockInfo;
|
|
44
|
-
throwSecurityException(sourceContext);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
};
|
|
54
|
+
if (!results) return;
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
'path-traversal',
|
|
53
|
-
function(result, sinkContext) {
|
|
56
|
+
for (const result of results) {
|
|
54
57
|
const idx = sinkContext.value.indexOf(result.value);
|
|
55
|
-
|
|
58
|
+
const findings = idx !== -1 ? { path: sinkContext.value } : null;
|
|
59
|
+
|
|
60
|
+
if (findings) {
|
|
61
|
+
handleFindings(sourceContext, sinkContext, ruleId, result, findings);
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
|
-
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
inputTracing.handleCommandInjection = function(sourceContext, sinkContext) {
|
|
67
|
+
const ruleId = 'cmd-injection';
|
|
68
|
+
const results = getResultsByRuleId(ruleId, sourceContext);
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
if (!results) return;
|
|
71
|
+
|
|
72
|
+
for (const result of results) {
|
|
73
|
+
let findings = null;
|
|
62
74
|
const inputIndex = sinkContext.value.indexOf(result.value);
|
|
63
75
|
if (inputIndex !== -1) {
|
|
64
|
-
|
|
76
|
+
findings = agentLib.checkCommandInjectionSink(
|
|
65
77
|
inputIndex,
|
|
66
78
|
result.value.length,
|
|
67
79
|
sinkContext.value,
|
|
68
80
|
);
|
|
69
81
|
}
|
|
82
|
+
|
|
83
|
+
if (findings) {
|
|
84
|
+
handleFindings(sourceContext, sinkContext, ruleId, result, findings);
|
|
85
|
+
}
|
|
70
86
|
}
|
|
71
|
-
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
inputTracing.handleSqlInjection = function(sourceContext, sinkContext) {
|
|
90
|
+
const ruleId = 'sql-injection';
|
|
91
|
+
const results = getResultsByRuleId(ruleId, sourceContext);
|
|
72
92
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let
|
|
93
|
+
if (!results) return;
|
|
94
|
+
|
|
95
|
+
for (const result of results) {
|
|
96
|
+
let findings = null;
|
|
77
97
|
|
|
78
98
|
const inputIndex = sinkContext.value.indexOf(result.value);
|
|
99
|
+
|
|
79
100
|
// if the user input is not in the sink input, there is nothing to do.
|
|
80
101
|
if (inputIndex === -1) {
|
|
81
|
-
|
|
102
|
+
continue;
|
|
82
103
|
}
|
|
83
104
|
|
|
84
105
|
if (inputIndex === 0 && sinkContext.value === result.value) {
|
|
85
|
-
|
|
106
|
+
findings = {
|
|
86
107
|
startIndex: 0,
|
|
87
108
|
endIndex: result.value.length - 1,
|
|
88
109
|
overrunIndex: 0,
|
|
89
110
|
boundaryIndex: 0,
|
|
90
111
|
};
|
|
91
112
|
} else {
|
|
92
|
-
|
|
113
|
+
findings = agentLib.checkSqlInjectionSink(
|
|
93
114
|
inputIndex,
|
|
94
115
|
result.value.length,
|
|
95
116
|
2,
|
|
@@ -97,21 +118,95 @@ module.exports = function(core) {
|
|
|
97
118
|
);
|
|
98
119
|
}
|
|
99
120
|
|
|
100
|
-
|
|
121
|
+
if (findings) {
|
|
122
|
+
handleFindings(sourceContext, sinkContext, ruleId, result, findings);
|
|
123
|
+
}
|
|
101
124
|
}
|
|
102
|
-
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
inputTracing.nosqlInjectionMongo = function(sourceContext, sinkContext) {
|
|
128
|
+
const ruleId = 'nosql-injection-mongo';
|
|
129
|
+
const expansionResults = getResultsByRuleId(ruleId, sourceContext);
|
|
130
|
+
const stringResults = getResultsByRuleId('ssjs-injection', sourceContext);
|
|
103
131
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
132
|
+
if (expansionResults) {
|
|
133
|
+
let expansionFindings = null;
|
|
134
|
+
for (const result of expansionResults) {
|
|
135
|
+
expansionFindings = handleObjectValue(result, sinkContext.value);
|
|
107
136
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
if (expansionFindings) {
|
|
138
|
+
handleFindings(sourceContext, sinkContext, ruleId, result, expansionFindings);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
112
141
|
}
|
|
113
|
-
|
|
142
|
+
|
|
143
|
+
if (stringResults) {
|
|
144
|
+
let stringFindings = null;
|
|
145
|
+
for (const result of stringResults) {
|
|
146
|
+
stringFindings = handleStringValue(result, sinkContext.value);
|
|
147
|
+
|
|
148
|
+
if (stringFindings) {
|
|
149
|
+
handleFindings(sourceContext, sinkContext, ruleId, result, stringFindings);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
inputTracing.ssjsInjection = function(sourceContext, sinkContext) {
|
|
156
|
+
// 'ssjs-injection' NYI
|
|
157
|
+
return null;
|
|
158
|
+
};
|
|
114
159
|
|
|
115
160
|
return inputTracing;
|
|
116
161
|
};
|
|
117
162
|
|
|
163
|
+
function handleObjectValue(result, object) {
|
|
164
|
+
if (typeof object !== 'object') {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
let findings = null;
|
|
168
|
+
simpleTraverse(object, function(path, type, value) {
|
|
169
|
+
if (type !== 'Key' || findings) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// the result value is the key that was found
|
|
173
|
+
if (result.key === value) {
|
|
174
|
+
// does the object at this path equal the user input?
|
|
175
|
+
let obj = object;
|
|
176
|
+
for (const p of path) {
|
|
177
|
+
obj = obj[p];
|
|
178
|
+
}
|
|
179
|
+
obj = obj[value];
|
|
180
|
+
// does the found object in the query equal the saved object?
|
|
181
|
+
if (util.isDeepStrictEqual(obj, result.mongoContext.inputToCheck)) {
|
|
182
|
+
findings = { key: value };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return findings;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function handleStringValue(result, string) {
|
|
191
|
+
if (typeof string !== 'string') {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
let findings = null;
|
|
195
|
+
|
|
196
|
+
const inputIndex = string.indexOf(result.value);
|
|
197
|
+
// if the user input is not in the sink input, there is nothing to do.
|
|
198
|
+
if (inputIndex === -1) {
|
|
199
|
+
return findings;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (inputIndex === 0 && string === result.value) {
|
|
203
|
+
findings = {
|
|
204
|
+
startIndex: 0,
|
|
205
|
+
endIndex: result.value.length - 1,
|
|
206
|
+
overrunIndex: 0,
|
|
207
|
+
boundaryIndex: 0,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return findings;
|
|
212
|
+
}
|
|
@@ -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
|
/**
|
|
@@ -19,12 +34,14 @@ module.exports = function(core) {
|
|
|
19
34
|
require('./install/child-process')(core);
|
|
20
35
|
require('./install/mysql')(core);
|
|
21
36
|
require('./install/postgres')(core);
|
|
37
|
+
require('./install/mongodb')(core);
|
|
22
38
|
|
|
23
39
|
inputTracing.install = function() {
|
|
24
40
|
inputTracing.fsInstrumentation.install();
|
|
25
41
|
inputTracing.cpInstrumentation.install();
|
|
26
42
|
inputTracing.mysqlInstrumentation.install();
|
|
27
43
|
inputTracing.postgresInstrumentation.install();
|
|
44
|
+
inputTracing.mongodbInstrumentation.install();
|
|
28
45
|
// TODO: NODE-2360 (2260?)
|
|
29
46
|
};
|
|
30
47
|
|
|
@@ -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 { isString } = require('@contrast/common');
|
|
@@ -30,7 +45,7 @@ module.exports = function(core) {
|
|
|
30
45
|
|
|
31
46
|
const sinkContext = captureStacktrace(
|
|
32
47
|
{ name, value },
|
|
33
|
-
{ constructorOpt: data.hooked }
|
|
48
|
+
{ constructorOpt: data.hooked, prependFrames: [data.orig] }
|
|
34
49
|
);
|
|
35
50
|
inputTracing.handleCommandInjection(sourceContext, sinkContext);
|
|
36
51
|
}
|