@contrast/agent 4.9.1 → 4.10.3
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/bin/VERSION +1 -1
- package/bin/linux/contrast-service +0 -0
- package/bin/mac/contrast-service +0 -0
- package/bin/windows/contrast-service.exe +0 -0
- package/bootstrap.js +2 -2
- package/lib/assess/propagators/joi/any.js +48 -0
- package/lib/assess/propagators/joi/index.js +2 -0
- package/lib/assess/propagators/joi/object.js +61 -0
- package/lib/assess/propagators/joi/string-base.js +16 -0
- package/lib/assess/propagators/mongoose/helpers.js +36 -1
- package/lib/assess/propagators/mongoose/index.js +1 -0
- package/lib/assess/propagators/mongoose/map.js +19 -31
- package/lib/assess/propagators/mongoose/mixed.js +71 -0
- package/lib/assess/propagators/mongoose/string.js +8 -0
- package/lib/core/arch-components/mysql.js +25 -15
- package/lib/core/arch-components/postgres.js +36 -26
- package/lib/core/arch-components/util.js +49 -0
- package/lib/core/config/options.js +11 -9
- package/lib/core/express/index.js +8 -3
- package/lib/core/fastify/index.js +2 -1
- package/lib/core/hapi/index.js +2 -1
- package/lib/core/koa/index.js +9 -1
- package/lib/core/logger/debug-logger.js +4 -3
- package/lib/core/rewrite/binary-expression.js +1 -2
- package/lib/core/rewrite/callees.js +19 -2
- package/lib/core/rewrite/catch-clause.js +4 -2
- package/lib/core/rewrite/import-declaration.js +71 -0
- package/lib/core/rewrite/index.js +10 -7
- package/lib/core/rewrite/injections.js +6 -4
- package/lib/core/rewrite/rewrite-log.js +5 -8
- package/lib/protect/restify/sources.js +35 -0
- package/lib/protect/rules/nosqli/nosql-injection-rule.js +30 -16
- package/lib/protect/rules/nosqli/nosql-scanner/index.js +1 -1
- package/lib/protect/rules/nosqli/nosql-scanner/rethinkdbscanner.js +26 -0
- package/lib/protect/sinks/index.js +2 -0
- package/lib/protect/sinks/mongodb.js +1 -3
- package/lib/protect/sinks/rethinkdb.js +47 -0
- package/package.json +23 -15
package/lib/core/hapi/index.js
CHANGED
package/lib/core/koa/index.js
CHANGED
|
@@ -216,13 +216,21 @@ class KoaCore {
|
|
|
216
216
|
*/
|
|
217
217
|
registerRequestHandler(ctx) {
|
|
218
218
|
ctx.req.request = ctx.request;
|
|
219
|
+
const { query } = ctx.request;
|
|
219
220
|
ctx.request = new Proxy(ctx.request, {
|
|
220
221
|
set(...args) {
|
|
221
222
|
const [obj, prop] = args;
|
|
222
223
|
const result = Reflect.set(...args);
|
|
224
|
+
if (query) {
|
|
225
|
+
decorateRequest({
|
|
226
|
+
query
|
|
227
|
+
});
|
|
228
|
+
}
|
|
223
229
|
switch (prop) {
|
|
224
230
|
case 'body':
|
|
225
|
-
decorateRequest({
|
|
231
|
+
decorateRequest({
|
|
232
|
+
body: obj[prop]
|
|
233
|
+
});
|
|
226
234
|
agentEmitter.emit(constants.EVENTS.CTX_REQUEST_BODY, {
|
|
227
235
|
request: obj,
|
|
228
236
|
ctx
|
|
@@ -108,9 +108,10 @@ class DebugLogFactory {
|
|
|
108
108
|
|
|
109
109
|
// We always log to a file, but check whether we should log to stdout
|
|
110
110
|
if (
|
|
111
|
-
|
|
112
|
-
!
|
|
113
|
-
|
|
111
|
+
this.loggerPath &&
|
|
112
|
+
(!this.stdout ||
|
|
113
|
+
!process.env.DEBUG ||
|
|
114
|
+
!/(^|,\s*)contrast:.+/.test(process.env.DEBUG))
|
|
114
115
|
) {
|
|
115
116
|
this.mute = true;
|
|
116
117
|
}
|
|
@@ -15,7 +15,6 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const t = require('@babel/types');
|
|
18
|
-
const _ = require('lodash');
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* Wraps binary expressions in one of the following: `ContrastMethods.__add`,
|
|
@@ -30,7 +29,7 @@ const _ = require('lodash');
|
|
|
30
29
|
* @param {import('.').State} state
|
|
31
30
|
*/
|
|
32
31
|
module.exports = function BinaryExpression(path, state) {
|
|
33
|
-
const spec =
|
|
32
|
+
const spec = state.specs.find(({ token }) => token === path.node.operator);
|
|
34
33
|
if (
|
|
35
34
|
!spec ||
|
|
36
35
|
!state.callees[spec.name] ||
|
|
@@ -15,7 +15,6 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const { expression } = require('@babel/template');
|
|
18
|
-
const _ = require('lodash');
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* @typedef {Object} Spec
|
|
@@ -67,6 +66,22 @@ const specs = [
|
|
|
67
66
|
token: 'eval',
|
|
68
67
|
modes: { assess: true, protect: true }
|
|
69
68
|
},
|
|
69
|
+
// Import Declaration
|
|
70
|
+
{
|
|
71
|
+
name: '__importDefault',
|
|
72
|
+
type: 'ImportDefaultSpecifier',
|
|
73
|
+
modes: { assess: true, protect: true }
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: '__importNamespace',
|
|
77
|
+
type: 'ImportNamespaceSpecifier',
|
|
78
|
+
modes: { assess: true, protect: true }
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: '__import',
|
|
82
|
+
type: 'ImportSpecifier',
|
|
83
|
+
modes: { assess: true, protect: true }
|
|
84
|
+
},
|
|
70
85
|
// Member Expression
|
|
71
86
|
{
|
|
72
87
|
name: '__forceCopy',
|
|
@@ -116,7 +131,9 @@ module.exports = function createCallees(agent) {
|
|
|
116
131
|
const callees = specs.reduce(
|
|
117
132
|
(memo, spec) =>
|
|
118
133
|
(assessMode && spec.modes.assess) || (protectMode && spec.modes.protect)
|
|
119
|
-
?
|
|
134
|
+
? Object.assign(memo, {
|
|
135
|
+
[spec.name]: calleeBuilder({ name: spec.name })
|
|
136
|
+
})
|
|
120
137
|
: memo,
|
|
121
138
|
{}
|
|
122
139
|
);
|
|
@@ -13,7 +13,6 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
13
13
|
way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
15
|
const { statement } = require('@babel/template');
|
|
16
|
-
const _ = require('lodash');
|
|
17
16
|
|
|
18
17
|
const logStatementBuilder = statement(
|
|
19
18
|
`if (global.CONTRAST_LOG) {
|
|
@@ -38,7 +37,10 @@ const logStatementBuilder = statement(
|
|
|
38
37
|
* @param {import('.').State} state
|
|
39
38
|
*/
|
|
40
39
|
module.exports = function CatchClause(path, state) {
|
|
41
|
-
|
|
40
|
+
const { config } = state.agent;
|
|
41
|
+
if (!config || !config.agent.node.enable_catch_log) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
42
44
|
|
|
43
45
|
path.node.param = path.node.param || path.scope.generateUidIdentifier('err');
|
|
44
46
|
path
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const t = require('@babel/types');
|
|
18
|
+
|
|
19
|
+
const IMPORT_META_URL_MEMBER_EXPRESSION = t.memberExpression(
|
|
20
|
+
t.memberExpression(t.identifier('import'), t.identifier('meta')),
|
|
21
|
+
t.identifier('url')
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Appends calls to our own instrumentable import methods, providing access to
|
|
26
|
+
* the imported modules.
|
|
27
|
+
* ```
|
|
28
|
+
* import x from 'mod';
|
|
29
|
+
* // becomes:
|
|
30
|
+
* import x from 'mod';
|
|
31
|
+
* ContrastMethods.__importDefault(x, 'mod', import.meta.url);
|
|
32
|
+
*
|
|
33
|
+
* import * as x from 'mod';
|
|
34
|
+
* // becomes:
|
|
35
|
+
* import x from 'mod';
|
|
36
|
+
* ContrastMethods.__importNamespace(x, 'mod', import.meta.url);
|
|
37
|
+
*
|
|
38
|
+
* import { foo, bar as baz } from 'mod';;
|
|
39
|
+
* // becomes
|
|
40
|
+
* ContrastMethods.__import(foo, 'foo', 'mod', import.meta.url);
|
|
41
|
+
* ContrastMethods.__import(baz, 'bar', 'mod', import.meta.url);
|
|
42
|
+
* ```
|
|
43
|
+
* @param {import('@babel/traverse').NodePath<import('@babel/types').ImportDeclaration>} path
|
|
44
|
+
* @param {import('.').State} state
|
|
45
|
+
*/
|
|
46
|
+
module.exports = function ImportDeclaration(path, state) {
|
|
47
|
+
const { source, specifiers } = path.node;
|
|
48
|
+
|
|
49
|
+
path.insertAfter(
|
|
50
|
+
specifiers.map((importSpec) => {
|
|
51
|
+
const spec = state.specs.find(({ type }) => type === importSpec.type);
|
|
52
|
+
|
|
53
|
+
if (!spec || !state.callees[spec.name]) return;
|
|
54
|
+
|
|
55
|
+
const args = [importSpec.local];
|
|
56
|
+
|
|
57
|
+
if (t.isImportSpecifier(importSpec)) {
|
|
58
|
+
args.push(
|
|
59
|
+
t.isIdentifier(importSpec.imported)
|
|
60
|
+
? t.stringLiteral(importSpec.imported.name)
|
|
61
|
+
: importSpec.imported
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
args.push(source);
|
|
66
|
+
args.push(IMPORT_META_URL_MEMBER_EXPRESSION);
|
|
67
|
+
|
|
68
|
+
return t.callExpression(state.callees[spec.name], args);
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -18,23 +18,24 @@ const { default: generate } = require('@babel/generator');
|
|
|
18
18
|
const { parse } = require('@babel/parser');
|
|
19
19
|
const { default: traverse } = require('@babel/traverse');
|
|
20
20
|
|
|
21
|
+
const RewriteDeadzones = require('../../assess/deadzones/rewrite');
|
|
22
|
+
const { util: sourceMapUtility } = require('../../util/source-map');
|
|
21
23
|
const Scopes = require('../async-storage/scopes');
|
|
22
24
|
const logger = require('../logger')('contrast:rewrite');
|
|
23
|
-
const RewriteDeadzones = require('../../assess/deadzones/rewrite');
|
|
24
|
-
const logRewrite = require('./log');
|
|
25
|
-
const RewriteLog = require('./rewrite-log');
|
|
26
|
-
const isContrastMethod = require('./is-contrast-method');
|
|
27
|
-
const functionWrap = require('./function-wrap');
|
|
28
|
-
const prependGlobals = require('./prepend-globals');
|
|
29
25
|
const AssignmentExpression = require('./assignment-expression');
|
|
30
26
|
const BinaryExpression = require('./binary-expression');
|
|
31
27
|
const CallExpression = require('./call-expression');
|
|
32
28
|
const CatchClause = require('./catch-clause');
|
|
29
|
+
const functionWrap = require('./function-wrap');
|
|
30
|
+
const ImportDeclaration = require('./import-declaration');
|
|
31
|
+
const isContrastMethod = require('./is-contrast-method');
|
|
32
|
+
const logRewrite = require('./log');
|
|
33
33
|
const MemberExpression = require('./member-expression');
|
|
34
34
|
const ObjectProperty = require('./object-property');
|
|
35
|
+
const prependGlobals = require('./prepend-globals');
|
|
36
|
+
const RewriteLog = require('./rewrite-log');
|
|
35
37
|
const SwitchStatement = require('./switch-statement');
|
|
36
38
|
const TemplateLiteral = require('./template-literal');
|
|
37
|
-
const sourceMapUtility = require('../../util/source-map').util;
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* @typedef {Object} State
|
|
@@ -105,6 +106,7 @@ class Rewriter {
|
|
|
105
106
|
BinaryExpression,
|
|
106
107
|
CallExpression,
|
|
107
108
|
CatchClause,
|
|
109
|
+
ImportDeclaration,
|
|
108
110
|
MemberExpression,
|
|
109
111
|
ObjectProperty,
|
|
110
112
|
SwitchStatement,
|
|
@@ -114,6 +116,7 @@ class Rewriter {
|
|
|
114
116
|
null,
|
|
115
117
|
initialState
|
|
116
118
|
);
|
|
119
|
+
traverse.cache.clear();
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
/**
|
|
@@ -14,7 +14,6 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const _ = require('lodash');
|
|
18
17
|
const logger = require('../logger')('contrast:rewrite:injections');
|
|
19
18
|
const patcher = require('../../hooks/patcher');
|
|
20
19
|
const { PATCH_TYPES } = require('../../constants');
|
|
@@ -100,7 +99,11 @@ const ContrastMethods = new Injection(null, 'ContrastMethods', {
|
|
|
100
99
|
},
|
|
101
100
|
__contrastEval: function __contrastEval(str) {
|
|
102
101
|
return str;
|
|
103
|
-
}
|
|
102
|
+
},
|
|
103
|
+
// TODO: NODE-2020
|
|
104
|
+
__importDefault(mod, source, url) {},
|
|
105
|
+
__importNamespace(mod, source, url) {},
|
|
106
|
+
__import(mod, spec, source, url) {}
|
|
104
107
|
});
|
|
105
108
|
|
|
106
109
|
ContrastMethods.enable();
|
|
@@ -157,8 +160,7 @@ module.exports = {
|
|
|
157
160
|
* @returns {Injection[]}
|
|
158
161
|
*/
|
|
159
162
|
getEnabled() {
|
|
160
|
-
return
|
|
161
|
-
injections,
|
|
163
|
+
return Object.values(injections).reduce(
|
|
162
164
|
(enabled, injection) =>
|
|
163
165
|
injection.enabled() ? [...enabled, injection] : enabled,
|
|
164
166
|
[]
|
|
@@ -13,7 +13,6 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
13
13
|
way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
|
-
const _ = require('lodash');
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Helper class that provides some means of optimizing the rewrite process.
|
|
@@ -32,11 +31,9 @@ module.exports = class RewriteLog {
|
|
|
32
31
|
this._tokenMatches = specs.reduce(
|
|
33
32
|
(matches, spec) =>
|
|
34
33
|
callees[spec.name]
|
|
35
|
-
?
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
!spec.token || 0 <= codeString.indexOf(spec.token)
|
|
39
|
-
)
|
|
34
|
+
? Object.assign(matches, {
|
|
35
|
+
[spec.name]: !spec.token || 0 <= codeString.indexOf(spec.token)
|
|
36
|
+
})
|
|
40
37
|
: matches,
|
|
41
38
|
{}
|
|
42
39
|
);
|
|
@@ -60,7 +57,7 @@ module.exports = class RewriteLog {
|
|
|
60
57
|
* if we need to even rewrite the code at all.
|
|
61
58
|
*/
|
|
62
59
|
foundTokens() {
|
|
63
|
-
return
|
|
60
|
+
return Object.values(this._tokenMatches).some(Boolean);
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
/**
|
|
@@ -72,6 +69,6 @@ module.exports = class RewriteLog {
|
|
|
72
69
|
* make changes, we can just return original code.
|
|
73
70
|
*/
|
|
74
71
|
rewritesOccurred() {
|
|
75
|
-
return !this.aborted &&
|
|
72
|
+
return !this.aborted && Object.values(this._tokensRewritten).some(Boolean);
|
|
76
73
|
}
|
|
77
74
|
};
|
|
@@ -21,6 +21,8 @@ const {
|
|
|
21
21
|
} = require('../../constants');
|
|
22
22
|
const { emitSourceEvent } = require('../../hooks/frameworks/common');
|
|
23
23
|
const agentEmitter = require('../../agent-emitter');
|
|
24
|
+
const patcher = require('../../hooks/patcher');
|
|
25
|
+
const { PATCH_TYPES } = require('../../constants');
|
|
24
26
|
const {
|
|
25
27
|
utils,
|
|
26
28
|
constants: { EVENTS }
|
|
@@ -39,6 +41,39 @@ class RestifyProtectSources {
|
|
|
39
41
|
EVENTS.CREATE_SERVER,
|
|
40
42
|
this.registerUrlParamsHandler.bind(this)
|
|
41
43
|
);
|
|
44
|
+
agentEmitter.on(
|
|
45
|
+
EVENTS.RESTIFY_EXPORT,
|
|
46
|
+
this.registerQueryParamsHandler.bind(this)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Patches query parser middleware to wrap the callback argument. Once this
|
|
53
|
+
* wrapped callback is invoked w/out error we know we can proxy `req.query`.
|
|
54
|
+
* @param {object} restify
|
|
55
|
+
*/
|
|
56
|
+
registerQueryParamsHandler(restify) {
|
|
57
|
+
const {
|
|
58
|
+
plugins: { queryParser: origQueryParser }
|
|
59
|
+
} = restify;
|
|
60
|
+
|
|
61
|
+
if (origQueryParser) {
|
|
62
|
+
patcher.patch(restify.plugins, 'queryParser', {
|
|
63
|
+
name: 'restify.plugins',
|
|
64
|
+
patchType: PATCH_TYPES.FRAMEWORK,
|
|
65
|
+
alwaysRun: true,
|
|
66
|
+
post(data) {
|
|
67
|
+
patcher.patch(data, 'result', {
|
|
68
|
+
name: 'restify.plugins.queryParser',
|
|
69
|
+
patchType: PATCH_TYPES.PROTECT_SOURCE,
|
|
70
|
+
alwaysRun: true,
|
|
71
|
+
post(data) {
|
|
72
|
+
decorateRequest({ query: get(data.args[0], 'query') });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
42
77
|
}
|
|
43
78
|
}
|
|
44
79
|
|
|
@@ -26,9 +26,11 @@ const brackets = /\[(.{1,1024}?)\]/;
|
|
|
26
26
|
const child = /\[(.{1,1024}?)\]/g;
|
|
27
27
|
|
|
28
28
|
const MONGODB = 'mongodb';
|
|
29
|
+
const RETHINKDB = 'rethinkdb';
|
|
29
30
|
|
|
30
31
|
const ScannerKit = new Map([
|
|
31
|
-
[MONGODB, () => require('../nosqli/nosql-scanner').create('MongoDB')]
|
|
32
|
+
[MONGODB, () => require('../nosqli/nosql-scanner').create('MongoDB')],
|
|
33
|
+
[RETHINKDB, () => require('../nosqli/nosql-scanner').create('RethinkDB')]
|
|
32
34
|
]);
|
|
33
35
|
const SubstringFinder = require('../base-scanner/substring-finder');
|
|
34
36
|
|
|
@@ -144,27 +146,14 @@ class NoSqlInjectionRule extends require('../') {
|
|
|
144
146
|
_documentType === constants.INPUT_TYPES.PARAMETER_NAME &&
|
|
145
147
|
_documentPath.length <= 1024
|
|
146
148
|
) {
|
|
147
|
-
|
|
148
|
-
const parent = segment
|
|
149
|
-
? _documentPath.slice(0, segment.index)
|
|
150
|
-
: _documentPath;
|
|
151
|
-
|
|
152
|
-
pathArray.push('query');
|
|
153
|
-
|
|
154
|
-
if (parent) {
|
|
155
|
-
pathArray.push(parent);
|
|
156
|
-
}
|
|
157
|
-
while ((segment = child.exec(_documentPath))) {
|
|
158
|
-
pathArray.push(segment[1]);
|
|
159
|
-
}
|
|
160
|
-
pathArray.pop();
|
|
149
|
+
this.buildPathArray(_documentPath, pathArray);
|
|
161
150
|
} else {
|
|
162
151
|
// only qs params and body are "expandable"
|
|
163
152
|
pathArray.push('body', ..._documentPath.split('.'));
|
|
164
153
|
}
|
|
165
154
|
|
|
166
155
|
let data;
|
|
167
|
-
let curr = ctx.
|
|
156
|
+
let curr = ctx.request;
|
|
168
157
|
|
|
169
158
|
for (const path of pathArray) {
|
|
170
159
|
if (curr) {
|
|
@@ -175,6 +164,31 @@ class NoSqlInjectionRule extends require('../') {
|
|
|
175
164
|
return data;
|
|
176
165
|
}
|
|
177
166
|
|
|
167
|
+
buildPathArray(documentPath, pathArray) {
|
|
168
|
+
const documentPathArray = documentPath.split('.');
|
|
169
|
+
|
|
170
|
+
pathArray.push('query');
|
|
171
|
+
documentPathArray.forEach((dotSegment) => {
|
|
172
|
+
let lastChar;
|
|
173
|
+
let bracketSegment = brackets.exec(dotSegment);
|
|
174
|
+
let parent = bracketSegment
|
|
175
|
+
? dotSegment.slice(0, bracketSegment.index)
|
|
176
|
+
: dotSegment;
|
|
177
|
+
if (parent) {
|
|
178
|
+
lastChar = parent.slice(-1);
|
|
179
|
+
parent = lastChar === ']' ? parent.slice(0, -1) : parent;
|
|
180
|
+
pathArray.push(parent);
|
|
181
|
+
}
|
|
182
|
+
while ((bracketSegment = child.exec(dotSegment))) {
|
|
183
|
+
lastChar = bracketSegment[1].slice(-1);
|
|
184
|
+
const segment =
|
|
185
|
+
lastChar === ']' ? bracketSegment[1].slice(0, -1) : bracketSegment[1];
|
|
186
|
+
pathArray.push(segment);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
pathArray.pop();
|
|
190
|
+
}
|
|
191
|
+
|
|
178
192
|
getScanner(id) {
|
|
179
193
|
if (!ScannerKit.has(id)) {
|
|
180
194
|
throw new Error(`Unknown NoSQL scanner: ${id}`);
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
* RethinkDB scanner module
|
|
17
|
+
* @module lib/protect/rules/nosql/nosql-scanner/RethinkDBScanner
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const { BaseScanner } = require('../../base-scanner');
|
|
23
|
+
|
|
24
|
+
class RethinkDBScanner extends BaseScanner {}
|
|
25
|
+
|
|
26
|
+
module.exports = RethinkDBScanner;
|
|
@@ -29,6 +29,7 @@ const MongoDBSink = require('./mongodb');
|
|
|
29
29
|
const MySQLSink = require('./mysql');
|
|
30
30
|
const NodeSerializeSink = require('./node-serialize');
|
|
31
31
|
const PostgresSink = require('./postgres');
|
|
32
|
+
const RethinkDBSink = require('./rethinkdb');
|
|
32
33
|
const SequelizeSink = require('./sequelize');
|
|
33
34
|
const Sqlite3Sink = require('./sqlite3');
|
|
34
35
|
const VMSink = require('./vm');
|
|
@@ -46,6 +47,7 @@ module.exports = function init(agent) {
|
|
|
46
47
|
new MySQLSink(agent).install({ ModuleHook });
|
|
47
48
|
new NodeSerializeSink(agent).install({ ModuleHook });
|
|
48
49
|
new PostgresSink(agent).install({ ModuleHook });
|
|
50
|
+
new RethinkDBSink(agent).install({ ModuleHook });
|
|
49
51
|
new SequelizeSink(agent).install({ ModuleHook });
|
|
50
52
|
new VMSink(agent).install({ ModuleHook });
|
|
51
53
|
new Sqlite3Sink(agent).install({ ModuleHook });
|
|
@@ -21,8 +21,6 @@ const BaseSensor = require('../../hooks/frameworks/base');
|
|
|
21
21
|
const patcher = require('../../hooks/patcher');
|
|
22
22
|
const { PATCH_TYPES } = require('../../constants');
|
|
23
23
|
const { emitSinkEvent } = require('../../hooks/frameworks/common');
|
|
24
|
-
const agentEmitter = require('../../agent-emitter');
|
|
25
|
-
const SinkEvent = require('../models/sink-event');
|
|
26
24
|
|
|
27
25
|
const { SINK_TYPES } = constants;
|
|
28
26
|
const ID = 'mongodb';
|
|
@@ -37,7 +35,7 @@ function getCursorQueryData(args, version) {
|
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
if (_.isString(query)) {
|
|
40
|
-
return query.toString()
|
|
38
|
+
return query.toString();
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
if (query['$where']) {
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const constants = require('../../constants');
|
|
18
|
+
const BaseSensor = require('../../hooks/frameworks/base');
|
|
19
|
+
const patcher = require('../../hooks/patcher');
|
|
20
|
+
const { PATCH_TYPES } = require('../../constants');
|
|
21
|
+
const { emitSinkEvent } = require('../../hooks/frameworks/common');
|
|
22
|
+
|
|
23
|
+
const { SINK_TYPES } = constants;
|
|
24
|
+
const ID = 'rethinkdb';
|
|
25
|
+
|
|
26
|
+
class RethinkDBSensor extends BaseSensor {
|
|
27
|
+
constructor(agent) {
|
|
28
|
+
super({ id: ID, agent });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
install({ ModuleHook }) {
|
|
32
|
+
ModuleHook.resolve({ name: 'rethinkdb' }, (rethinkdb) => {
|
|
33
|
+
patcher.patch(rethinkdb, 'js', {
|
|
34
|
+
alwaysRun: true,
|
|
35
|
+
name: 'rethinkdb',
|
|
36
|
+
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
37
|
+
pre: (data) => {
|
|
38
|
+
emitSinkEvent(data.args[0], SINK_TYPES.NOSQL_QUERY, ID);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = RethinkDBSensor;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agent",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.3",
|
|
4
4
|
"description": "Node.js security instrumentation by Contrast Security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -19,7 +19,13 @@
|
|
|
19
19
|
"Griffin Yourick",
|
|
20
20
|
"Ati Ok",
|
|
21
21
|
"Pat McClory",
|
|
22
|
-
"Michael Woytowitz"
|
|
22
|
+
"Michael Woytowitz",
|
|
23
|
+
"Jacob Kaplan",
|
|
24
|
+
"Yulia Tenincheva",
|
|
25
|
+
"Bruce MacNaughton",
|
|
26
|
+
"Krasen Penchev",
|
|
27
|
+
"Ivaylo Grahlyov",
|
|
28
|
+
"Yavor Stoychev"
|
|
23
29
|
],
|
|
24
30
|
"scripts": {
|
|
25
31
|
"docs": "jsdoc -c ../.jsdoc.json",
|
|
@@ -72,10 +78,10 @@
|
|
|
72
78
|
"@babel/types": "^7.12.1",
|
|
73
79
|
"@contrast/distringuish-prebuilt": "^2.2.0",
|
|
74
80
|
"@contrast/flat": "^4.1.1",
|
|
75
|
-
"@contrast/fn-inspect": "^2.4.
|
|
81
|
+
"@contrast/fn-inspect": "^2.4.4",
|
|
76
82
|
"@contrast/heapdump": "^1.1.0",
|
|
77
|
-
"@contrast/protobuf-api": "^3.2.
|
|
78
|
-
"@contrast/require-hook": "^2.0.
|
|
83
|
+
"@contrast/protobuf-api": "^3.2.3",
|
|
84
|
+
"@contrast/require-hook": "^2.0.8",
|
|
79
85
|
"@contrast/synchronous-source-maps": "^1.1.0",
|
|
80
86
|
"amqp-connection-manager": "^3.2.2",
|
|
81
87
|
"amqplib": "^0.8.0",
|
|
@@ -107,12 +113,14 @@
|
|
|
107
113
|
"devDependencies": {
|
|
108
114
|
"@aws-sdk/client-dynamodb": "^3.39.0",
|
|
109
115
|
"@bmacnaughton/string-generator": "^1.0.0",
|
|
110
|
-
"@contrast/eslint-config": "^2.0
|
|
116
|
+
"@contrast/eslint-config": "^2.2.0",
|
|
111
117
|
"@contrast/fake-module": "file:test/mock/contrast-fake",
|
|
112
|
-
"@contrast/screener-service": "^1.12.
|
|
118
|
+
"@contrast/screener-service": "^1.12.9",
|
|
113
119
|
"@hapi/boom": "file:test/mock/boom",
|
|
114
120
|
"@hapi/hapi": "file:test/mock/hapi",
|
|
115
121
|
"@ls-lint/ls-lint": "^1.8.1",
|
|
122
|
+
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
|
123
|
+
"@typescript-eslint/parser": "^5.10.2",
|
|
116
124
|
"ajv": "^8.5.0",
|
|
117
125
|
"ast-types": "^0.12.4",
|
|
118
126
|
"aws-sdk": "file:test/mock/aws-sdk",
|
|
@@ -129,11 +137,11 @@
|
|
|
129
137
|
"dustjs-linkedin": "^3.0.1",
|
|
130
138
|
"ejs": "^3.1.6",
|
|
131
139
|
"escape-html": "^1.0.3",
|
|
132
|
-
"eslint": "^8.
|
|
133
|
-
"eslint-config-prettier": "^
|
|
134
|
-
"eslint-plugin-mocha": "^
|
|
140
|
+
"eslint": "^8.8.0",
|
|
141
|
+
"eslint-config-prettier": "^8.3.0",
|
|
142
|
+
"eslint-plugin-mocha": "^10.0.3",
|
|
135
143
|
"eslint-plugin-node": "^11.1.0",
|
|
136
|
-
"eslint-plugin-prettier": "^
|
|
144
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
137
145
|
"express": "file:test/mock/express",
|
|
138
146
|
"fetch-cookie": "^0.11.0",
|
|
139
147
|
"form-data": "^3.0.0",
|
|
@@ -142,13 +150,13 @@
|
|
|
142
150
|
"husky": "^6.0.0",
|
|
143
151
|
"inquirer": "^8.1.2",
|
|
144
152
|
"joi": "^17.4.0",
|
|
145
|
-
"jsdoc": "^3.6.
|
|
153
|
+
"jsdoc": "^3.6.10",
|
|
146
154
|
"libxmljs": "file:test/mock/libxmljs",
|
|
147
155
|
"libxmljs2": "file:test/mock/libxmljs2",
|
|
148
156
|
"lint-staged": "^12.0.2",
|
|
149
157
|
"madge": "^4.0.1",
|
|
150
158
|
"marsdb": "file:test/mock/marsdb",
|
|
151
|
-
"mocha": "^9.
|
|
159
|
+
"mocha": "^9.2.0",
|
|
152
160
|
"mochawesome": "^7.0.1",
|
|
153
161
|
"mongodb": "file:test/mock/mongodb",
|
|
154
162
|
"mongodb-npm": "npm:mongodb@^3.6.5",
|
|
@@ -156,13 +164,13 @@
|
|
|
156
164
|
"mustache": "^3.0.1",
|
|
157
165
|
"mysql": "file:test/mock/mysql",
|
|
158
166
|
"nock": "^12.0.3",
|
|
159
|
-
"node-fetch": "^2.6.
|
|
167
|
+
"node-fetch": "^2.6.7",
|
|
160
168
|
"node-serialize": "file:test/mock/node-serialize",
|
|
161
169
|
"npm-license-crawler": "^0.2.1",
|
|
162
170
|
"nyc": "^15.1.0",
|
|
163
171
|
"pg": "file:test/mock/pg",
|
|
164
172
|
"pino": "^6.7.0",
|
|
165
|
-
"prettier": "^
|
|
173
|
+
"prettier": "^2.5.1",
|
|
166
174
|
"proxyquire": "^2.1.0",
|
|
167
175
|
"qs": "^6.9.4",
|
|
168
176
|
"rethinkdb": "file:test/mock/rethinkdb",
|