@contrast/agent 4.8.0 → 4.10.1
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 +12 -2
- package/esm.mjs +33 -0
- package/lib/assess/index.js +2 -0
- package/lib/assess/models/source-event.js +6 -0
- package/lib/assess/policy/rules.json +29 -0
- package/lib/assess/policy/signatures.json +6 -0
- package/lib/assess/propagators/JSON/stringify.js +77 -7
- 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/assess/sinks/rethinkdb-nosql-injection.js +142 -0
- package/lib/assess/sources/event-handler.js +307 -0
- package/lib/assess/sources/index.js +93 -5
- package/lib/assess/spdy/index.js +23 -0
- package/lib/assess/spdy/sinks/index.js +23 -0
- package/lib/assess/spdy/sinks/xss.js +84 -0
- package/lib/assess/technologies/index.js +2 -1
- package/lib/constants.js +2 -1
- package/lib/contrast.js +6 -6
- package/lib/core/arch-components/index.js +1 -0
- package/lib/core/arch-components/mongodb.js +22 -18
- package/lib/core/arch-components/mysql.js +25 -15
- package/lib/core/arch-components/postgres.js +40 -12
- package/lib/core/arch-components/sqlite3.js +3 -5
- package/lib/core/arch-components/util.js +49 -0
- package/lib/core/config/options.js +37 -1
- package/lib/core/exclusions/exclusion.js +2 -5
- package/lib/core/express/index.js +28 -2
- package/lib/core/express/utils.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/hooks/frameworks/index.js +2 -0
- package/lib/hooks/frameworks/spdy.js +87 -0
- package/lib/hooks/http.js +11 -0
- 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/reporter/translations/to-protobuf/dtm/trace-event/index.js +4 -4
- package/lib/util/source-map.js +3 -3
- package/package.json +18 -12
|
@@ -0,0 +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
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const ReflectedXss = require('./xss');
|
|
18
|
+
|
|
19
|
+
module.exports = class SpdySinks {
|
|
20
|
+
constructor(agent) {
|
|
21
|
+
new ReflectedXss(agent);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
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 agentEmitter = require('../../../agent-emitter');
|
|
18
|
+
const { HTTP_RESPONSE_HOOKED_METHOD_KEYS } = require('../../../constants');
|
|
19
|
+
const policy = require('../../policy');
|
|
20
|
+
const { Signature, CallContext } = require('../../models');
|
|
21
|
+
|
|
22
|
+
class SpdyXss {
|
|
23
|
+
constructor(agent) {
|
|
24
|
+
this.common = require('../../sinks/common')(agent);
|
|
25
|
+
this.rules = policy.rules;
|
|
26
|
+
this.ruleId = 'reflected-xss';
|
|
27
|
+
this.signature = new Signature({
|
|
28
|
+
moduleName: 'spdy.response',
|
|
29
|
+
methodName: 'push',
|
|
30
|
+
isModule: false
|
|
31
|
+
});
|
|
32
|
+
agentEmitter.on(
|
|
33
|
+
HTTP_RESPONSE_HOOKED_METHOD_KEYS.PUSH,
|
|
34
|
+
this.checkResult.bind(this)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* checks if an assess rule is enabled in policy
|
|
40
|
+
*/
|
|
41
|
+
get enabled() {
|
|
42
|
+
return (
|
|
43
|
+
this.rules &&
|
|
44
|
+
this.rules['reflected-xss'] &&
|
|
45
|
+
this.rules['reflected-xss'].enabled
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
checkResult(body) {
|
|
50
|
+
if (!this.enabled) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { ruleId, signature } = this;
|
|
55
|
+
|
|
56
|
+
const {
|
|
57
|
+
isVulnerable,
|
|
58
|
+
xss: { disallowedTags },
|
|
59
|
+
requiredTags,
|
|
60
|
+
report
|
|
61
|
+
} = this.common;
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
isVulnerable({
|
|
65
|
+
input: body,
|
|
66
|
+
disallowedTags,
|
|
67
|
+
requiredTags,
|
|
68
|
+
ruleId
|
|
69
|
+
})
|
|
70
|
+
) {
|
|
71
|
+
const ctxt = new CallContext({
|
|
72
|
+
obj: body,
|
|
73
|
+
args: [body],
|
|
74
|
+
result: body,
|
|
75
|
+
stackOpts: {
|
|
76
|
+
constructorOpt: agentEmitter.emit
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
report({ ruleId, signature, input: body, ctxt });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = SpdyXss;
|
package/lib/constants.js
CHANGED
package/lib/contrast.js
CHANGED
|
@@ -178,7 +178,7 @@ contrastAgent.configureGlobalLogger = function(config, args, target = global) {
|
|
|
178
178
|
|
|
179
179
|
function getAgentArgs(options) {
|
|
180
180
|
const agentArgs = {};
|
|
181
|
-
|
|
181
|
+
program.options.forEach((opt) => {
|
|
182
182
|
if (opt.name() !== 'application.args' && options[opt.name()]) {
|
|
183
183
|
agentArgs[opt.name()] = options[opt.name()];
|
|
184
184
|
}
|
|
@@ -243,8 +243,8 @@ contrastAgent.prepare = function(...args) {
|
|
|
243
243
|
|
|
244
244
|
logger.info('Using config file at %s', config.configFile);
|
|
245
245
|
// log the argv before and after modification.
|
|
246
|
-
logger.info(`Original argv: ${
|
|
247
|
-
logger.info(`Modified argv: ${
|
|
246
|
+
logger.info(`Original argv: ${program.rawArgs.join(', ')}`);
|
|
247
|
+
logger.info(`Modified argv: ${program.args.join(', ')}`);
|
|
248
248
|
|
|
249
249
|
agent.config = config;
|
|
250
250
|
agent.tsFeatureSet.config = config;
|
|
@@ -335,12 +335,12 @@ contrastAgent.init = async function(args, isCli = false) {
|
|
|
335
335
|
// source: args passed to cli, destination: args after cli parsed it
|
|
336
336
|
.action(async function callPrepare(options, commanderArgs = []) {
|
|
337
337
|
// the user app main differs if a runner vs preload
|
|
338
|
-
script = isCli ?
|
|
338
|
+
script = isCli ? program.args[0] : program.rawArgs[1];
|
|
339
339
|
options.script = script;
|
|
340
340
|
// need to slice off app main in runner mode
|
|
341
341
|
options['application.args'] = isCli
|
|
342
|
-
?
|
|
343
|
-
:
|
|
342
|
+
? program.args.slice(1)
|
|
343
|
+
: program.args;
|
|
344
344
|
|
|
345
345
|
try {
|
|
346
346
|
enabled = await contrastAgent.prepare(options, commanderArgs, isCli);
|
|
@@ -28,25 +28,29 @@ ModuleHook.resolve(
|
|
|
28
28
|
patchType: PATCH_TYPES.ARCH_COMPONENT,
|
|
29
29
|
alwaysRun: true,
|
|
30
30
|
post(ctx) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (servers.length === 0) {
|
|
34
|
-
logger.warn('Unable to find any MongoDB servers\n');
|
|
35
|
-
}
|
|
36
|
-
for (const server of servers) {
|
|
37
|
-
agentEmitter.emit('architectureComponent', {
|
|
38
|
-
vendor: 'MongoDB',
|
|
39
|
-
url: `mongodb://${server.host}`,
|
|
40
|
-
remoteHost: '',
|
|
41
|
-
remotePort: server.port
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
} catch (err) {
|
|
45
|
-
logger.warn(
|
|
46
|
-
'unable to report MongoDB architecture component\n%o',
|
|
47
|
-
err
|
|
48
|
-
);
|
|
31
|
+
if (!ctx.result || !ctx.result.then) {
|
|
32
|
+
return;
|
|
49
33
|
}
|
|
34
|
+
|
|
35
|
+
// We should report only when connection is successful
|
|
36
|
+
ctx.result.then(function(client) {
|
|
37
|
+
try {
|
|
38
|
+
const { servers = [] } = ctx.obj.s && ctx.obj.s.options;
|
|
39
|
+
for (const server of servers) {
|
|
40
|
+
agentEmitter.emit('architectureComponent', {
|
|
41
|
+
vendor: 'MongoDB',
|
|
42
|
+
url: `mongodb://${server.host}:${server.port}`,
|
|
43
|
+
remoteHost: '',
|
|
44
|
+
remotePort: server.port
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
} catch (err) {
|
|
48
|
+
logger.warn(
|
|
49
|
+
'unable to report MongoDB architecture component\n%o',
|
|
50
|
+
err
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
50
54
|
}
|
|
51
55
|
});
|
|
52
56
|
}
|
|
@@ -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,19 +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
|
-
|
|
33
|
-
|
|
29
|
+
waitToConnect(wrapCtx)
|
|
30
|
+
.then(() => {
|
|
31
|
+
try {
|
|
32
|
+
const {
|
|
33
|
+
host = process.env.PGHOST,
|
|
34
|
+
port = process.env.PGPORT
|
|
35
|
+
} = wrapCtx.result;
|
|
36
|
+
|
|
37
|
+
if (!host) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let url = host;
|
|
42
|
+
|
|
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
|
+
}
|
|
50
|
+
|
|
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
|
+
);
|
|
34
68
|
});
|
|
35
|
-
} catch (err) {
|
|
36
|
-
logger.warn(
|
|
37
|
-
'unable to report PostgreSQL architecture component\n',
|
|
38
|
-
err
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
69
|
}
|
|
42
70
|
})
|
|
43
71
|
);
|
|
@@ -13,6 +13,7 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
13
13
|
way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
|
+
|
|
16
17
|
const patcher = require('../../hooks/patcher');
|
|
17
18
|
const ModuleHook = require('../../hooks/require');
|
|
18
19
|
const agentEmitter = require('../../agent-emitter');
|
|
@@ -26,17 +27,14 @@ ModuleHook.resolve({ name: 'sqlite3' }, (sqlite3) => {
|
|
|
26
27
|
alwaysRun: true,
|
|
27
28
|
post(wrapCtx) {
|
|
28
29
|
try {
|
|
29
|
-
// can either be a path to a file or `:memory:'.
|
|
30
|
-
const url = new URL(wrapCtx.args[0]).toString();
|
|
31
|
-
|
|
32
30
|
agentEmitter.emit('architectureComponent', {
|
|
33
31
|
vendor: 'SQLite3',
|
|
34
|
-
url,
|
|
32
|
+
url: wrapCtx.args[0],
|
|
35
33
|
remoteHost: '',
|
|
36
34
|
remotePort: 0
|
|
37
35
|
});
|
|
38
36
|
} catch (err) {
|
|
39
|
-
logger.warn('unable to report SQLite3 architecture component\n', err);
|
|
37
|
+
logger.warn('unable to report SQLite3 architecture component\n%o', err);
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
});
|
|
@@ -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
|
+
};
|
|
@@ -33,7 +33,8 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
33
33
|
* @module lib/core/config/options
|
|
34
34
|
*/
|
|
35
35
|
'use strict';
|
|
36
|
-
const
|
|
36
|
+
const { Command, Option } = require('commander');
|
|
37
|
+
const program = new Command();
|
|
37
38
|
const os = require('os');
|
|
38
39
|
const url = require('url');
|
|
39
40
|
const path = require('path');
|
|
@@ -486,6 +487,18 @@ const agent = [
|
|
|
486
487
|
desc:
|
|
487
488
|
'set limit for stack trace size (larger limits will improve accuracy but increase memory usage)'
|
|
488
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
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: 'agent.traverse_and_track',
|
|
498
|
+
arg: '<traverse-and-track>',
|
|
499
|
+
default: false,
|
|
500
|
+
desc: 'source membrane alternative'
|
|
501
|
+
},
|
|
489
502
|
{
|
|
490
503
|
name: 'agent.polling.app_activity_ms',
|
|
491
504
|
arg: '<ms>',
|
|
@@ -967,6 +980,16 @@ if (process.env.CONTRAST_DEV) {
|
|
|
967
980
|
}
|
|
968
981
|
];
|
|
969
982
|
}
|
|
983
|
+
const sails = [
|
|
984
|
+
{
|
|
985
|
+
name: 'pathToSails',
|
|
986
|
+
arg: '<path>'
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
name: 'gdsrc',
|
|
990
|
+
arg: '<path>'
|
|
991
|
+
}
|
|
992
|
+
];
|
|
970
993
|
|
|
971
994
|
const options = [].concat(
|
|
972
995
|
misc,
|
|
@@ -1008,6 +1031,19 @@ options.forEach((option) => {
|
|
|
1008
1031
|
program.option(name, option.desc);
|
|
1009
1032
|
});
|
|
1010
1033
|
|
|
1034
|
+
// In NODE-2059 it was discovered that a module was appending config options that the
|
|
1035
|
+
// agent didn't recognize and was causing the application to not load properly.
|
|
1036
|
+
// The agent doesn't need to do anything with these options. It just needs to not
|
|
1037
|
+
// throw an error when it encounters them but we also don't need them displayed on
|
|
1038
|
+
// the agent's config option list. The newest version of Commander lets us do exactly this.
|
|
1039
|
+
// This is structured so that if anything like this is discovered again, they can be
|
|
1040
|
+
// added in easily.
|
|
1041
|
+
const hiddenOptions = [].concat(sails);
|
|
1042
|
+
|
|
1043
|
+
hiddenOptions.forEach((option) => {
|
|
1044
|
+
program.addOption(new Option(`--${option.name} ${option.arg}`).hideHelp());
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1011
1047
|
function getDefault(optionName) {
|
|
1012
1048
|
let option;
|
|
1013
1049
|
options.forEach((entry) => {
|
|
@@ -31,12 +31,9 @@ class Exclusion {
|
|
|
31
31
|
return this.assess && this.appliesToRule(id, this.assessmentRulesList);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// When an exclusion applies to all rules, its rules list is empty
|
|
34
35
|
appliesToRule(id, list) {
|
|
35
|
-
|
|
36
|
-
const appliesToAllRules = list.length === 0;
|
|
37
|
-
const appliesToRuleId = list.includes(id);
|
|
38
|
-
|
|
39
|
-
return appliesToAllRules || appliesToRuleId;
|
|
36
|
+
return list.length === 0 || list.includes(id);
|
|
40
37
|
}
|
|
41
38
|
|
|
42
39
|
appliesToAllAssessRules() {
|
|
@@ -120,6 +120,24 @@ class ExpressFramework {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
|
+
|
|
124
|
+
patcher.patch(express.response, 'push', {
|
|
125
|
+
name: 'express.response.push',
|
|
126
|
+
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
127
|
+
pre(data) {
|
|
128
|
+
agentEmitter.emit(
|
|
129
|
+
EVENTS.REQUEST_SEND,
|
|
130
|
+
data.args[0],
|
|
131
|
+
SINK_TYPES.RESPONSE_BODY
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const body = data.args[0];
|
|
135
|
+
if (isString(body)) {
|
|
136
|
+
emitSendEvent(body.valueOf());
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
123
141
|
patcher.patch(express.response, 'end', {
|
|
124
142
|
name: 'express.response.end',
|
|
125
143
|
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
@@ -310,7 +328,9 @@ class ExpressFramework {
|
|
|
310
328
|
}, 'textParser');
|
|
311
329
|
|
|
312
330
|
this.useAfter(function ContrastBodyParsed(req, res, next) {
|
|
313
|
-
agentEmitter.emit(EVENTS.BODY_PARSED, req, res,
|
|
331
|
+
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, {
|
|
332
|
+
type: INPUT_TYPES.BODY
|
|
333
|
+
});
|
|
314
334
|
next();
|
|
315
335
|
}, 'urlencodedParser');
|
|
316
336
|
|
|
@@ -356,7 +376,7 @@ class ExpressFramework {
|
|
|
356
376
|
const self = this;
|
|
357
377
|
|
|
358
378
|
// Hook the request handler so that we can access the top of each route.
|
|
359
|
-
// This is the only place the "params" hash is
|
|
379
|
+
// This is the only place the "params" hash is available
|
|
360
380
|
const Layer = ExpressFramework.getStack(app)[0].constructor;
|
|
361
381
|
const _handle = Layer.prototype.handle_request;
|
|
362
382
|
if (_handle) {
|
|
@@ -377,6 +397,9 @@ class ExpressFramework {
|
|
|
377
397
|
req[LAYER_STACK].pop();
|
|
378
398
|
}
|
|
379
399
|
});
|
|
400
|
+
if (req.query) {
|
|
401
|
+
decorateRequest({ query: req.query });
|
|
402
|
+
}
|
|
380
403
|
const params = new Object(this.params);
|
|
381
404
|
if (Object.keys(params).length) {
|
|
382
405
|
agentEmitter.emit(
|
|
@@ -399,9 +422,12 @@ class ExpressFramework {
|
|
|
399
422
|
|
|
400
423
|
Whatever the core issue is, it doesn't appear to have any effects
|
|
401
424
|
elsewhere in any of our Express/Kraken framework support.
|
|
425
|
+
|
|
426
|
+
BODY_PARSED event is emitted to support Sails framework
|
|
402
427
|
*/
|
|
403
428
|
if (req.body) {
|
|
404
429
|
decorateRequest({ body: req.body });
|
|
430
|
+
agentEmitter.emit(EVENTS.BODY_PARSED, req, res, req.body);
|
|
405
431
|
}
|
|
406
432
|
}
|
|
407
433
|
});
|
|
@@ -33,7 +33,7 @@ const {
|
|
|
33
33
|
|
|
34
34
|
const EVENTS = {
|
|
35
35
|
REQUEST_READY: 'Express.RequestReady',
|
|
36
|
-
BODY_PARSED: 'Express.
|
|
36
|
+
BODY_PARSED: 'Express.bodyParsed',
|
|
37
37
|
COOKIES_PARSED: 'Express.cookiesParsed',
|
|
38
38
|
PARAMS_PARSED: 'Express.paramsParsed',
|
|
39
39
|
REQUEST_SEND: 'Express.requestSend',
|
|
@@ -246,7 +246,9 @@ const captureSignature = (layer, signature) => {
|
|
|
246
246
|
const normalizeSignatureArg = (arg) => {
|
|
247
247
|
// we weave in middleware for express that is prefixed with Contrast
|
|
248
248
|
// remove this from signature
|
|
249
|
-
if (
|
|
249
|
+
if (arg === '') {
|
|
250
|
+
return null;
|
|
251
|
+
} else if (typeof arg === 'function' && arg.name.startsWith('Contrast')) {
|
|
250
252
|
return null;
|
|
251
253
|
} else if (typeof arg === 'function') {
|
|
252
254
|
return getHandlerName(arg);
|
|
@@ -313,8 +315,11 @@ const updateRouterSignatures = (self, router, path) => {
|
|
|
313
315
|
return;
|
|
314
316
|
}
|
|
315
317
|
const routePath = _.get(route, 'path', '');
|
|
316
|
-
const routeHandle = _.get(route, 'stack[0].handle');
|
|
318
|
+
const routeHandle = _.get(route, 'stack[0].handle', '');
|
|
317
319
|
const routeMethod = _.get(route, 'stack[0].method');
|
|
320
|
+
if (!routeMethod) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
318
323
|
const newSignature = createSignature(self, 'Router', routeMethod, [
|
|
319
324
|
path,
|
|
320
325
|
routePath,
|
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',
|