@contrast/agent 4.9.0 → 4.10.2
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/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/rewrite/callees.js +16 -0
- package/lib/core/rewrite/import-declaration.js +71 -0
- package/lib/core/rewrite/index.js +9 -7
- package/lib/core/rewrite/injections.js +5 -1
- 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/lib/util/source-map.js +3 -3
- package/package.json +16 -15
package/bin/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.28.
|
|
1
|
+
2.28.9
|
|
Binary file
|
package/bin/mac/contrast-service
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
const requireHook = require('../../../hooks/require');
|
|
16
|
+
const patcher = require('../../../hooks/patcher');
|
|
17
|
+
const {
|
|
18
|
+
PATCH_TYPES: { ASSESS_PROPAGATOR }
|
|
19
|
+
} = require('../../../constants');
|
|
20
|
+
const tracker = require('../../../tracker');
|
|
21
|
+
const agent = require('../../../agent');
|
|
22
|
+
|
|
23
|
+
requireHook.resolve(
|
|
24
|
+
{ name: 'joi', file: 'lib/types/any.js', version: '>=17.0.0' },
|
|
25
|
+
(object) => {
|
|
26
|
+
if (
|
|
27
|
+
!agent.config ||
|
|
28
|
+
(agent.config && !agent.config.agent.trust_custom_validators)
|
|
29
|
+
) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
patcher.patch(object._definition.rules.custom, 'validate', {
|
|
34
|
+
name: 'joi.any.custom.validate',
|
|
35
|
+
patchType: ASSESS_PROPAGATOR,
|
|
36
|
+
alwaysRun: true,
|
|
37
|
+
post(data) {
|
|
38
|
+
if (data.result && typeof data.result === 'string') {
|
|
39
|
+
tracker.untrack(data.result);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (data.args[0] && typeof data.args[0] === 'string') {
|
|
43
|
+
tracker.untrack(data.args[0]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
);
|
|
@@ -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
|
+
const requireHook = require('../../../hooks/require');
|
|
16
|
+
const patcher = require('../../../hooks/patcher');
|
|
17
|
+
const {
|
|
18
|
+
PATCH_TYPES: { ASSESS_PROPAGATOR }
|
|
19
|
+
} = require('../../../constants');
|
|
20
|
+
const tracker = require('../../../tracker');
|
|
21
|
+
const agent = require('../../../agent');
|
|
22
|
+
|
|
23
|
+
const traverseObjectAndUntrack = (object, incomingInput, result) => {
|
|
24
|
+
if (object.type === 'object' && object.$_terms.keys) {
|
|
25
|
+
object.$_terms.keys.forEach(({ key, schema }) => {
|
|
26
|
+
traverseObjectAndUntrack(schema, incomingInput[key], result[key]);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
object.type === 'string' &&
|
|
32
|
+
Array.isArray(object.$_terms.externals) &&
|
|
33
|
+
object.$_terms.externals.length
|
|
34
|
+
) {
|
|
35
|
+
tracker.untrack(incomingInput);
|
|
36
|
+
tracker.untrack(result);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
requireHook.resolve(
|
|
41
|
+
{ name: 'joi', file: 'lib/types/object.js', version: '>=17.0.0' },
|
|
42
|
+
(object) => {
|
|
43
|
+
if (
|
|
44
|
+
!agent.config ||
|
|
45
|
+
(agent.config && !agent.config.agent.trust_custom_validators)
|
|
46
|
+
) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
patcher.patch(object.__proto__, 'validateAsync', {
|
|
51
|
+
name: 'joi.object.validateAsync',
|
|
52
|
+
patchType: ASSESS_PROPAGATOR,
|
|
53
|
+
alwaysRun: true,
|
|
54
|
+
post(data) {
|
|
55
|
+
data.result.then((result) =>
|
|
56
|
+
traverseObjectAndUntrack(data.obj, data.args[0], result)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
);
|
|
@@ -24,6 +24,7 @@ const tracker = require('../../../tracker');
|
|
|
24
24
|
const { PropagationEvent, Signature, CallContext } = require('../../models');
|
|
25
25
|
const TagRange = require('../../models/tag-range');
|
|
26
26
|
const tagRangeUtil = require('../../models/tag-range/util');
|
|
27
|
+
const agent = require('../../../agent');
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Patch joi.string.validate so that it tags input with string-type-checked if validated
|
|
@@ -54,6 +55,21 @@ function instrumentJoiString(string) {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
});
|
|
58
|
+
|
|
59
|
+
if (agent.config.agent.trust_custom_validators) {
|
|
60
|
+
patcher.patch(string.__proto__, 'validateAsync', {
|
|
61
|
+
name: 'joi.string.validateAsync',
|
|
62
|
+
patchType: ASSESS_PROPAGATOR,
|
|
63
|
+
alwaysRun: true,
|
|
64
|
+
post(data) {
|
|
65
|
+
if (!data.obj.$_terms.externals.length) return;
|
|
66
|
+
data.result.then((result) => {
|
|
67
|
+
tracker.untrack(data.args[0]);
|
|
68
|
+
tracker.untrack(result);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
requireHook.resolve(
|
|
@@ -12,9 +12,44 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
12
12
|
engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
13
|
way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
|
+
const TagRange = require('../../models/tag-range');
|
|
16
|
+
const { CallContext, PropagationEvent, Signature } = require('../../models');
|
|
17
|
+
|
|
15
18
|
const hasUserDefinedValidator = (data) =>
|
|
16
19
|
data.obj.validators.some((validator) => validator.type === 'user defined');
|
|
17
20
|
|
|
21
|
+
const tagCustomValidatedValues = (values, data, tracker, tagRangeUtil) => {
|
|
22
|
+
for (const value of values) {
|
|
23
|
+
if (typeof value !== 'string') continue;
|
|
24
|
+
|
|
25
|
+
const trackingData = tracker.track(value);
|
|
26
|
+
|
|
27
|
+
if (!trackingData) return;
|
|
28
|
+
|
|
29
|
+
const { props } = trackingData;
|
|
30
|
+
const stringLength = value.length - 1;
|
|
31
|
+
|
|
32
|
+
props.tagRanges = tagRangeUtil.add(
|
|
33
|
+
props.tagRanges,
|
|
34
|
+
new TagRange(0, stringLength, 'custom-validated-nosql-injection')
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
props.tagRanges = tagRangeUtil.add(
|
|
38
|
+
props.tagRanges,
|
|
39
|
+
new TagRange(0, stringLength, 'string-type-checked')
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
props.event = new PropagationEvent({
|
|
43
|
+
context: new CallContext(data),
|
|
44
|
+
signature: new Signature('mongoose.mixed.doValidateSync'),
|
|
45
|
+
tagRanges: props.tagRanges,
|
|
46
|
+
source: 'P',
|
|
47
|
+
target: 'A',
|
|
48
|
+
parents: [props.event]
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
18
52
|
module.exports = {
|
|
19
|
-
hasUserDefinedValidator
|
|
53
|
+
hasUserDefinedValidator,
|
|
54
|
+
tagCustomValidatedValues
|
|
20
55
|
};
|
|
@@ -21,9 +21,11 @@ const tagRangeUtil = require('../../models/tag-range/util');
|
|
|
21
21
|
const {
|
|
22
22
|
PATCH_TYPES: { ASSESS_PROPAGATOR }
|
|
23
23
|
} = require('../../../constants');
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const {
|
|
25
|
+
hasUserDefinedValidator,
|
|
26
|
+
tagCustomValidatedValues
|
|
27
|
+
} = require('./helpers');
|
|
28
|
+
const agent = require('../../../agent');
|
|
27
29
|
|
|
28
30
|
const doValidateSyncPatcher = (SchemaMap) => {
|
|
29
31
|
patcher.patch(SchemaMap.prototype, 'doValidateSync', {
|
|
@@ -31,37 +33,16 @@ const doValidateSyncPatcher = (SchemaMap) => {
|
|
|
31
33
|
name: 'mongoose.map.doValidateSync',
|
|
32
34
|
patchType: ASSESS_PROPAGATOR,
|
|
33
35
|
post(data) {
|
|
34
|
-
if (data.result
|
|
36
|
+
if (data.result) return;
|
|
35
37
|
|
|
36
38
|
if (!hasUserDefinedValidator(data)) return;
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const stringLength = value.length - 1;
|
|
45
|
-
|
|
46
|
-
props.tagRanges = tagRangeUtil.add(
|
|
47
|
-
props.tagRanges,
|
|
48
|
-
new TagRange(0, stringLength, 'custom-validated-nosql-injection')
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
props.tagRanges = tagRangeUtil.add(
|
|
52
|
-
props.tagRanges,
|
|
53
|
-
new TagRange(0, stringLength, 'string-type-checked')
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
props.event = new PropagationEvent({
|
|
57
|
-
context: new CallContext(data),
|
|
58
|
-
signature: new Signature('mongoose.map.doValidateSync'),
|
|
59
|
-
tagRanges: props.tagRanges,
|
|
60
|
-
source: 'P',
|
|
61
|
-
target: 'A',
|
|
62
|
-
parents: [props.event]
|
|
63
|
-
});
|
|
64
|
-
}
|
|
40
|
+
tagCustomValidatedValues(
|
|
41
|
+
data.args[0].values(),
|
|
42
|
+
data,
|
|
43
|
+
tracker,
|
|
44
|
+
tagRangeUtil
|
|
45
|
+
);
|
|
65
46
|
}
|
|
66
47
|
});
|
|
67
48
|
};
|
|
@@ -69,6 +50,13 @@ const doValidateSyncPatcher = (SchemaMap) => {
|
|
|
69
50
|
requireHook.resolve(
|
|
70
51
|
{ name: 'mongoose', file: 'lib/schema/map.js', version: '>=5.0.0' },
|
|
71
52
|
(SchemaMap) => {
|
|
53
|
+
if (
|
|
54
|
+
!agent.config ||
|
|
55
|
+
(agent.config && !agent.config.agent.trust_custom_validators)
|
|
56
|
+
) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
72
60
|
doValidateSyncPatcher(SchemaMap);
|
|
73
61
|
}
|
|
74
62
|
);
|
|
@@ -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 tracker = require('../../../tracker');
|
|
18
|
+
const patcher = require('../../../hooks/patcher');
|
|
19
|
+
const requireHook = require('../../../hooks/require');
|
|
20
|
+
const tagRangeUtil = require('../../models/tag-range/util');
|
|
21
|
+
const {
|
|
22
|
+
PATCH_TYPES: { ASSESS_PROPAGATOR }
|
|
23
|
+
} = require('../../../constants');
|
|
24
|
+
const {
|
|
25
|
+
hasUserDefinedValidator,
|
|
26
|
+
tagCustomValidatedValues
|
|
27
|
+
} = require('./helpers');
|
|
28
|
+
const agent = require('../../../agent');
|
|
29
|
+
|
|
30
|
+
const doValidateSyncPatcher = (SchemaMap) => {
|
|
31
|
+
patcher.patch(SchemaMap.prototype, 'doValidateSync', {
|
|
32
|
+
alwaysRun: true,
|
|
33
|
+
name: 'mongoose.mixed.doValidateSync',
|
|
34
|
+
patchType: ASSESS_PROPAGATOR,
|
|
35
|
+
post(data) {
|
|
36
|
+
if (data.result || !hasUserDefinedValidator(data)) return;
|
|
37
|
+
|
|
38
|
+
const input = data.args[0];
|
|
39
|
+
const inputType = typeof input;
|
|
40
|
+
|
|
41
|
+
if (inputType !== 'string' && inputType !== 'object') return;
|
|
42
|
+
|
|
43
|
+
let values;
|
|
44
|
+
if (inputType === 'string') {
|
|
45
|
+
values = [input];
|
|
46
|
+
} else if (Array.isArray(input)) {
|
|
47
|
+
values = input;
|
|
48
|
+
} else if (input instanceof Map) {
|
|
49
|
+
values = input.values();
|
|
50
|
+
} else {
|
|
51
|
+
values = Object.values(input);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
tagCustomValidatedValues(values, data, tracker, tagRangeUtil);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
requireHook.resolve(
|
|
60
|
+
{ name: 'mongoose', file: 'lib/schema/mixed.js', version: '>=5.0.0' },
|
|
61
|
+
(SchemaMap) => {
|
|
62
|
+
if (
|
|
63
|
+
!agent.config ||
|
|
64
|
+
(agent.config && !agent.config.agent.trust_custom_validators)
|
|
65
|
+
) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
doValidateSyncPatcher(SchemaMap);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
@@ -24,6 +24,7 @@ const {
|
|
|
24
24
|
const TagRange = require('../../models/tag-range');
|
|
25
25
|
const { CallContext, PropagationEvent, Signature } = require('../../models');
|
|
26
26
|
const { hasUserDefinedValidator } = require('./helpers');
|
|
27
|
+
const agent = require('../../../agent');
|
|
27
28
|
|
|
28
29
|
const enumPatcher = (SchemaString) => {
|
|
29
30
|
patcher.patch(SchemaString.prototype, 'enum', {
|
|
@@ -98,6 +99,13 @@ const doValidateSyncPatcher = (SchemaString) => {
|
|
|
98
99
|
requireHook.resolve(
|
|
99
100
|
{ name: 'mongoose', file: 'lib/schema/string.js', version: '>=5.0.0' },
|
|
100
101
|
(SchemaString) => {
|
|
102
|
+
if (
|
|
103
|
+
!agent.config ||
|
|
104
|
+
(agent.config && !agent.config.agent.trust_custom_validators)
|
|
105
|
+
) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
101
109
|
enumPatcher(SchemaString);
|
|
102
110
|
doValidateSyncPatcher(SchemaString);
|
|
103
111
|
}
|
|
@@ -19,6 +19,7 @@ const ModuleHook = require('../../hooks/require');
|
|
|
19
19
|
const agentEmitter = require('../../agent-emitter');
|
|
20
20
|
const { PATCH_TYPES } = require('../../constants');
|
|
21
21
|
const logger = require('../logger')('contrast:arch-component');
|
|
22
|
+
const waitToConnect = require('./util');
|
|
22
23
|
|
|
23
24
|
ModuleHook.resolve(
|
|
24
25
|
{ name: 'mysql', file: 'lib/Connection.js' },
|
|
@@ -28,22 +29,31 @@ ModuleHook.resolve(
|
|
|
28
29
|
patchType: PATCH_TYPES.ARCH_COMPONENT,
|
|
29
30
|
alwaysRun: true,
|
|
30
31
|
post(wrapCtx) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
waitToConnect(wrapCtx)
|
|
33
|
+
.then(() => {
|
|
34
|
+
try {
|
|
35
|
+
let url = 'mysql://';
|
|
36
|
+
if (this.config.socketPath) {
|
|
37
|
+
url += this.config.socketPath;
|
|
38
|
+
} else {
|
|
39
|
+
url += `${this.config.host}`;
|
|
40
|
+
}
|
|
41
|
+
agentEmitter.emit('architectureComponent', {
|
|
42
|
+
vendor: 'MySQL',
|
|
43
|
+
url: new URL(url).toString(),
|
|
44
|
+
remoteHost: '',
|
|
45
|
+
remotePort: !this.config.socketPath ? this.config.port : -1
|
|
46
|
+
});
|
|
47
|
+
} catch (err) {
|
|
48
|
+
logger.warn(
|
|
49
|
+
'unable to report MySQL architecture component\n',
|
|
50
|
+
err
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.catch((err) => {
|
|
55
|
+
logger.warn('unable to report MySQL architecture component\n', err);
|
|
43
56
|
});
|
|
44
|
-
} catch (err) {
|
|
45
|
-
logger.warn('unable to report MySQL architecture component\n', err);
|
|
46
|
-
}
|
|
47
57
|
}
|
|
48
58
|
});
|
|
49
59
|
}
|
|
@@ -18,6 +18,7 @@ const ModuleHook = require('../../hooks/require');
|
|
|
18
18
|
const agentEmitter = require('../../agent-emitter');
|
|
19
19
|
const { PATCH_TYPES } = require('../../constants');
|
|
20
20
|
const logger = require('../logger')('contrast:arch-component');
|
|
21
|
+
const waitToConnect = require('./util');
|
|
21
22
|
|
|
22
23
|
ModuleHook.resolve({ name: 'pg', file: 'lib/client.js' }, (pgClient) =>
|
|
23
24
|
patcher.patch(pgClient, {
|
|
@@ -25,37 +26,46 @@ ModuleHook.resolve({ name: 'pg', file: 'lib/client.js' }, (pgClient) =>
|
|
|
25
26
|
patchType: PATCH_TYPES.ARCH_COMPONENT,
|
|
26
27
|
alwaysRun: true,
|
|
27
28
|
post(wrapCtx) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
waitToConnect(wrapCtx)
|
|
30
|
+
.then(() => {
|
|
31
|
+
try {
|
|
32
|
+
const {
|
|
33
|
+
host = process.env.PGHOST,
|
|
34
|
+
port = process.env.PGPORT
|
|
35
|
+
} = wrapCtx.result;
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
if (!host) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
let url = host;
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
// build protocol and port into url prior to parsing
|
|
44
|
+
if (url.indexOf('://') === -1) {
|
|
45
|
+
url = `postgresql://${url}`;
|
|
46
|
+
}
|
|
47
|
+
if (port !== undefined) {
|
|
48
|
+
url = `${url}:${port}`;
|
|
49
|
+
}
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
agentEmitter.emit('architectureComponent', {
|
|
52
|
+
vendor: 'PostgreSQL',
|
|
53
|
+
remotePort: port || 0,
|
|
54
|
+
url: new URL(url).toString()
|
|
55
|
+
});
|
|
56
|
+
} catch (err) {
|
|
57
|
+
logger.warn(
|
|
58
|
+
'unable to report PostgreSQL architecture component\n%o',
|
|
59
|
+
err
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
.catch((err) => {
|
|
64
|
+
logger.warn(
|
|
65
|
+
'unable to report PostgreSQL architecture component\n%o',
|
|
66
|
+
err
|
|
67
|
+
);
|
|
52
68
|
});
|
|
53
|
-
} catch (err) {
|
|
54
|
-
logger.warn(
|
|
55
|
-
'unable to report PostgreSQL architecture component\n%o',
|
|
56
|
-
err
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
69
|
}
|
|
60
70
|
})
|
|
61
71
|
);
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
const MYSQL = 'mysql.connect.arch_component';
|
|
16
|
+
const POSTGRES = 'pg.Client.arch_component';
|
|
17
|
+
|
|
18
|
+
module.exports = function waitToConnect(ctx, count = 0) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const maxAttempts = 10 * 60; // i.e., 1 min.
|
|
21
|
+
const checkConnection = setInterval(
|
|
22
|
+
function(ctx, resolve, reject) {
|
|
23
|
+
if (count >= maxAttempts) {
|
|
24
|
+
clearInterval(checkConnection);
|
|
25
|
+
reject();
|
|
26
|
+
}
|
|
27
|
+
switch (ctx.name) {
|
|
28
|
+
case MYSQL:
|
|
29
|
+
if (ctx.obj.state === 'authenticated') {
|
|
30
|
+
clearInterval(checkConnection);
|
|
31
|
+
resolve();
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
case POSTGRES:
|
|
35
|
+
if (ctx.result._connected) {
|
|
36
|
+
clearInterval(checkConnection);
|
|
37
|
+
resolve();
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
count++;
|
|
42
|
+
},
|
|
43
|
+
100,
|
|
44
|
+
ctx,
|
|
45
|
+
resolve,
|
|
46
|
+
reject
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
};
|
|
@@ -487,6 +487,12 @@ const agent = [
|
|
|
487
487
|
desc:
|
|
488
488
|
'set limit for stack trace size (larger limits will improve accuracy but increase memory usage)'
|
|
489
489
|
},
|
|
490
|
+
{
|
|
491
|
+
name: 'agent.trust_custom_validators',
|
|
492
|
+
arg: '<trust-custom-validators>',
|
|
493
|
+
default: false,
|
|
494
|
+
desc: `trust incoming strings when they pass custom validators (Mongoose, Joi)`
|
|
495
|
+
},
|
|
490
496
|
{
|
|
491
497
|
name: 'agent.traverse_and_track',
|
|
492
498
|
arg: '<traverse-and-track>',
|
|
@@ -977,11 +983,11 @@ if (process.env.CONTRAST_DEV) {
|
|
|
977
983
|
const sails = [
|
|
978
984
|
{
|
|
979
985
|
name: 'pathToSails',
|
|
980
|
-
arg: '<path>'
|
|
986
|
+
arg: '<path>'
|
|
981
987
|
},
|
|
982
988
|
{
|
|
983
989
|
name: 'gdsrc',
|
|
984
|
-
arg: '<path>'
|
|
990
|
+
arg: '<path>'
|
|
985
991
|
}
|
|
986
992
|
];
|
|
987
993
|
|
|
@@ -1030,16 +1036,12 @@ options.forEach((option) => {
|
|
|
1030
1036
|
// The agent doesn't need to do anything with these options. It just needs to not
|
|
1031
1037
|
// throw an error when it encounters them but we also don't need them displayed on
|
|
1032
1038
|
// the agent's config option list. The newest version of Commander lets us do exactly this.
|
|
1033
|
-
// This is structured so that if anything like this is discovered again, they can be
|
|
1039
|
+
// This is structured so that if anything like this is discovered again, they can be
|
|
1034
1040
|
// added in easily.
|
|
1035
|
-
const hiddenOptions = [].concat(
|
|
1036
|
-
sails
|
|
1037
|
-
)
|
|
1041
|
+
const hiddenOptions = [].concat(sails);
|
|
1038
1042
|
|
|
1039
1043
|
hiddenOptions.forEach((option) => {
|
|
1040
|
-
program.addOption(
|
|
1041
|
-
new Option(`--${option.name} ${option.arg}`).hideHelp()
|
|
1042
|
-
)
|
|
1044
|
+
program.addOption(new Option(`--${option.name} ${option.arg}`).hideHelp());
|
|
1043
1045
|
});
|
|
1044
1046
|
|
|
1045
1047
|
function getDefault(optionName) {
|
|
@@ -397,6 +397,9 @@ class ExpressFramework {
|
|
|
397
397
|
req[LAYER_STACK].pop();
|
|
398
398
|
}
|
|
399
399
|
});
|
|
400
|
+
if (req.query) {
|
|
401
|
+
decorateRequest({ query: req.query });
|
|
402
|
+
}
|
|
400
403
|
const params = new Object(this.params);
|
|
401
404
|
if (Object.keys(params).length) {
|
|
402
405
|
agentEmitter.emit(
|
|
@@ -419,12 +422,14 @@ class ExpressFramework {
|
|
|
419
422
|
|
|
420
423
|
Whatever the core issue is, it doesn't appear to have any effects
|
|
421
424
|
elsewhere in any of our Express/Kraken framework support.
|
|
422
|
-
|
|
423
|
-
BODY_PARSED event is emitted to support Sails framework
|
|
424
425
|
*/
|
|
425
426
|
if (req.body) {
|
|
426
427
|
decorateRequest({ body: req.body });
|
|
427
|
-
|
|
428
|
+
|
|
429
|
+
// BODY_PARSED event is emitted to support Sails framework
|
|
430
|
+
if (req._sails) {
|
|
431
|
+
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, req.body);
|
|
432
|
+
}
|
|
428
433
|
}
|
|
429
434
|
}
|
|
430
435
|
});
|
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
|
|
@@ -67,6 +67,22 @@ const specs = [
|
|
|
67
67
|
token: 'eval',
|
|
68
68
|
modes: { assess: true, protect: true }
|
|
69
69
|
},
|
|
70
|
+
// Import Declaration
|
|
71
|
+
{
|
|
72
|
+
name: '__importDefault',
|
|
73
|
+
type: 'ImportDefaultSpecifier',
|
|
74
|
+
modes: { assess: true, protect: true }
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: '__importNamespace',
|
|
78
|
+
type: 'ImportNamespaceSpecifier',
|
|
79
|
+
modes: { assess: true, protect: true }
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: '__import',
|
|
83
|
+
type: 'ImportSpecifier',
|
|
84
|
+
modes: { assess: true, protect: true }
|
|
85
|
+
},
|
|
70
86
|
// Member Expression
|
|
71
87
|
{
|
|
72
88
|
name: '__forceCopy',
|
|
@@ -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
|
+
const _ = require('lodash');
|
|
19
|
+
|
|
20
|
+
const IMPORT_META_URL_MEMBER_EXPRESSION = t.memberExpression(
|
|
21
|
+
t.memberExpression(t.identifier('import'), t.identifier('meta')),
|
|
22
|
+
t.identifier('url')
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Appends calls to our own instrumentable import methods, providing access to
|
|
27
|
+
* the imported modules.
|
|
28
|
+
* ```
|
|
29
|
+
* import x from 'mod';
|
|
30
|
+
* // becomes:
|
|
31
|
+
* import x from 'mod';
|
|
32
|
+
* ContrastMethods.__importDefault(x, 'mod', import.meta.url);
|
|
33
|
+
*
|
|
34
|
+
* import * as x from 'mod';
|
|
35
|
+
* // becomes:
|
|
36
|
+
* import x from 'mod';
|
|
37
|
+
* ContrastMethods.__importNamespace(x, 'mod', import.meta.url);
|
|
38
|
+
*
|
|
39
|
+
* import { foo, bar as baz } from 'mod';;
|
|
40
|
+
* // becomes
|
|
41
|
+
* ContrastMethods.__import(foo, 'foo', 'mod', import.meta.url);
|
|
42
|
+
* ContrastMethods.__import(baz, 'bar', 'mod', import.meta.url);
|
|
43
|
+
* ```
|
|
44
|
+
* @param {import('@babel/traverse').NodePath<import('@babel/types').ImportDeclaration>} path
|
|
45
|
+
* @param {import('.').State} state
|
|
46
|
+
*/
|
|
47
|
+
module.exports = function ImportDeclaration(path, state) {
|
|
48
|
+
const { source, specifiers } = path.node;
|
|
49
|
+
|
|
50
|
+
path.insertAfter(
|
|
51
|
+
specifiers.map((importSpec) => {
|
|
52
|
+
const spec = _.find(state.specs, { type: importSpec.type });
|
|
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,
|
|
@@ -100,7 +100,11 @@ const ContrastMethods = new Injection(null, 'ContrastMethods', {
|
|
|
100
100
|
},
|
|
101
101
|
__contrastEval: function __contrastEval(str) {
|
|
102
102
|
return str;
|
|
103
|
-
}
|
|
103
|
+
},
|
|
104
|
+
// TODO: NODE-2020
|
|
105
|
+
__importDefault(mod, source, url) {},
|
|
106
|
+
__importNamespace(mod, source, url) {},
|
|
107
|
+
__import(mod, spec, source, url) {}
|
|
104
108
|
});
|
|
105
109
|
|
|
106
110
|
ContrastMethods.enable();
|
|
@@ -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/lib/util/source-map.js
CHANGED
|
@@ -156,9 +156,9 @@ class SourceMapUtility {
|
|
|
156
156
|
* @returns {string} fixed filename with full path
|
|
157
157
|
*/
|
|
158
158
|
replaceSource(original, source) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
return original === source
|
|
160
|
+
? original
|
|
161
|
+
: original.replace(path.basename(original), source);
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agent",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.2",
|
|
4
4
|
"description": "Node.js security instrumentation by Contrast Security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -19,10 +19,15 @@
|
|
|
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
|
-
"preinstall": "npx npm-force-resolutions",
|
|
26
31
|
"docs": "jsdoc -c ../.jsdoc.json",
|
|
27
32
|
"release": "scripts/make-release.js",
|
|
28
33
|
"tag": "scripts/tag-release.js",
|
|
@@ -73,10 +78,10 @@
|
|
|
73
78
|
"@babel/types": "^7.12.1",
|
|
74
79
|
"@contrast/distringuish-prebuilt": "^2.2.0",
|
|
75
80
|
"@contrast/flat": "^4.1.1",
|
|
76
|
-
"@contrast/fn-inspect": "^2.4.
|
|
81
|
+
"@contrast/fn-inspect": "^2.4.4",
|
|
77
82
|
"@contrast/heapdump": "^1.1.0",
|
|
78
|
-
"@contrast/protobuf-api": "^3.2.
|
|
79
|
-
"@contrast/require-hook": "^2.0.
|
|
83
|
+
"@contrast/protobuf-api": "^3.2.3",
|
|
84
|
+
"@contrast/require-hook": "^2.0.8",
|
|
80
85
|
"@contrast/synchronous-source-maps": "^1.1.0",
|
|
81
86
|
"amqp-connection-manager": "^3.2.2",
|
|
82
87
|
"amqplib": "^0.8.0",
|
|
@@ -110,7 +115,7 @@
|
|
|
110
115
|
"@bmacnaughton/string-generator": "^1.0.0",
|
|
111
116
|
"@contrast/eslint-config": "^2.0.1",
|
|
112
117
|
"@contrast/fake-module": "file:test/mock/contrast-fake",
|
|
113
|
-
"@contrast/screener-service": "^1.12.
|
|
118
|
+
"@contrast/screener-service": "^1.12.9",
|
|
114
119
|
"@hapi/boom": "file:test/mock/boom",
|
|
115
120
|
"@hapi/hapi": "file:test/mock/hapi",
|
|
116
121
|
"@ls-lint/ls-lint": "^1.8.1",
|
|
@@ -143,13 +148,13 @@
|
|
|
143
148
|
"husky": "^6.0.0",
|
|
144
149
|
"inquirer": "^8.1.2",
|
|
145
150
|
"joi": "^17.4.0",
|
|
146
|
-
"jsdoc": "^3.6.
|
|
151
|
+
"jsdoc": "^3.6.10",
|
|
147
152
|
"libxmljs": "file:test/mock/libxmljs",
|
|
148
153
|
"libxmljs2": "file:test/mock/libxmljs2",
|
|
149
154
|
"lint-staged": "^12.0.2",
|
|
150
155
|
"madge": "^4.0.1",
|
|
151
156
|
"marsdb": "file:test/mock/marsdb",
|
|
152
|
-
"mocha": "^9.
|
|
157
|
+
"mocha": "^9.2.0",
|
|
153
158
|
"mochawesome": "^7.0.1",
|
|
154
159
|
"mongodb": "file:test/mock/mongodb",
|
|
155
160
|
"mongodb-npm": "npm:mongodb@^3.6.5",
|
|
@@ -157,7 +162,7 @@
|
|
|
157
162
|
"mustache": "^3.0.1",
|
|
158
163
|
"mysql": "file:test/mock/mysql",
|
|
159
164
|
"nock": "^12.0.3",
|
|
160
|
-
"node-fetch": "^2.6.
|
|
165
|
+
"node-fetch": "^2.6.7",
|
|
161
166
|
"node-serialize": "file:test/mock/node-serialize",
|
|
162
167
|
"npm-license-crawler": "^0.2.1",
|
|
163
168
|
"nyc": "^15.1.0",
|
|
@@ -191,9 +196,5 @@
|
|
|
191
196
|
"winston",
|
|
192
197
|
"winston-syslog",
|
|
193
198
|
"winston-daily-rotate-file"
|
|
194
|
-
]
|
|
195
|
-
"resolutions": {
|
|
196
|
-
"markdown-it": ">=12.3.2",
|
|
197
|
-
"marked": ">=4.0.10"
|
|
198
|
-
}
|
|
199
|
+
]
|
|
199
200
|
}
|