@contrast/agent 4.5.2 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assess/policy/propagators.json +20 -0
- package/lib/assess/policy/signatures.json +10 -0
- package/lib/core/config/options.js +3 -2
- package/lib/core/stacktrace.js +2 -1
- package/lib/hooks/frameworks/base.js +8 -2
- package/lib/hooks/frameworks/http.js +23 -16
- package/lib/hooks/frameworks/http2.js +73 -0
- package/lib/hooks/frameworks/index.js +8 -3
- package/lib/hooks/http.js +112 -128
- package/lib/hooks/require.js +16 -22
- package/lib/instrumentation.js +0 -3
- package/package.json +12 -15
- package/lib/hooks/frameworks/https.js +0 -42
- package/node_modules/bindings/LICENSE.md +0 -22
- package/node_modules/bindings/README.md +0 -98
- package/node_modules/bindings/bindings.js +0 -221
- package/node_modules/bindings/package.json +0 -32
- package/node_modules/file-uri-to-path/.npmignore +0 -1
- package/node_modules/file-uri-to-path/.travis.yml +0 -30
- package/node_modules/file-uri-to-path/History.md +0 -21
- package/node_modules/file-uri-to-path/LICENSE +0 -20
- package/node_modules/file-uri-to-path/README.md +0 -74
- package/node_modules/file-uri-to-path/index.d.ts +0 -2
- package/node_modules/file-uri-to-path/index.js +0 -66
- package/node_modules/file-uri-to-path/package.json +0 -36
- package/node_modules/file-uri-to-path/test/test.js +0 -24
- package/node_modules/file-uri-to-path/test/tests.json +0 -13
- package/node_modules/glossy/LICENSE +0 -19
- package/node_modules/glossy/README.md +0 -129
- package/node_modules/glossy/index.js +0 -12
- package/node_modules/glossy/lib/glossy/parse.js +0 -520
- package/node_modules/glossy/lib/glossy/produce.js +0 -459
- package/node_modules/glossy/package.json +0 -47
- package/node_modules/glossy/test/decide.js +0 -7
- package/node_modules/glossy/test/decode_pri.js +0 -24
- package/node_modules/glossy/test/parse_3164.js +0 -104
- package/node_modules/glossy/test/parse_5424.js +0 -106
- package/node_modules/glossy/test/parse_5848.js +0 -40
- package/node_modules/glossy/test/parse_8601.js +0 -14
- package/node_modules/glossy/test/parse_rfc3339.js +0 -9
- package/node_modules/glossy/test/produce.js +0 -162
- package/node_modules/glossy/test/runner.js +0 -40
- package/node_modules/glossy/test/structure_data.js +0 -24
- package/node_modules/nan/CHANGELOG.md +0 -537
- package/node_modules/nan/LICENSE.md +0 -13
- package/node_modules/nan/README.md +0 -455
- package/node_modules/nan/doc/asyncworker.md +0 -146
- package/node_modules/nan/doc/buffers.md +0 -54
- package/node_modules/nan/doc/callback.md +0 -76
- package/node_modules/nan/doc/converters.md +0 -41
- package/node_modules/nan/doc/errors.md +0 -226
- package/node_modules/nan/doc/json.md +0 -62
- package/node_modules/nan/doc/maybe_types.md +0 -583
- package/node_modules/nan/doc/methods.md +0 -664
- package/node_modules/nan/doc/new.md +0 -147
- package/node_modules/nan/doc/node_misc.md +0 -123
- package/node_modules/nan/doc/object_wrappers.md +0 -263
- package/node_modules/nan/doc/persistent.md +0 -296
- package/node_modules/nan/doc/scopes.md +0 -73
- package/node_modules/nan/doc/script.md +0 -38
- package/node_modules/nan/doc/string_bytes.md +0 -62
- package/node_modules/nan/doc/v8_internals.md +0 -199
- package/node_modules/nan/doc/v8_misc.md +0 -85
- package/node_modules/nan/include_dirs.js +0 -1
- package/node_modules/nan/nan.h +0 -2898
- package/node_modules/nan/nan_callbacks.h +0 -88
- package/node_modules/nan/nan_callbacks_12_inl.h +0 -514
- package/node_modules/nan/nan_callbacks_pre_12_inl.h +0 -520
- package/node_modules/nan/nan_converters.h +0 -72
- package/node_modules/nan/nan_converters_43_inl.h +0 -68
- package/node_modules/nan/nan_converters_pre_43_inl.h +0 -42
- package/node_modules/nan/nan_define_own_property_helper.h +0 -29
- package/node_modules/nan/nan_implementation_12_inl.h +0 -430
- package/node_modules/nan/nan_implementation_pre_12_inl.h +0 -263
- package/node_modules/nan/nan_json.h +0 -166
- package/node_modules/nan/nan_maybe_43_inl.h +0 -356
- package/node_modules/nan/nan_maybe_pre_43_inl.h +0 -268
- package/node_modules/nan/nan_new.h +0 -340
- package/node_modules/nan/nan_object_wrap.h +0 -156
- package/node_modules/nan/nan_persistent_12_inl.h +0 -132
- package/node_modules/nan/nan_persistent_pre_12_inl.h +0 -242
- package/node_modules/nan/nan_private.h +0 -73
- package/node_modules/nan/nan_string_bytes.h +0 -305
- package/node_modules/nan/nan_typedarray_contents.h +0 -96
- package/node_modules/nan/nan_weak.h +0 -437
- package/node_modules/nan/package.json +0 -41
- package/node_modules/nan/tools/1to2.js +0 -412
- package/node_modules/nan/tools/README.md +0 -14
- package/node_modules/nan/tools/package.json +0 -19
- package/node_modules/unix-dgram/LICENSE +0 -13
- package/node_modules/unix-dgram/README.md +0 -107
- package/node_modules/unix-dgram/binding.gyp +0 -20
- package/node_modules/unix-dgram/build/Makefile +0 -324
- package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram/src/unix_dgram.o.d +0 -58
- package/node_modules/unix-dgram/build/Release/.deps/Release/obj.target/unix_dgram.node.d +0 -1
- package/node_modules/unix-dgram/build/Release/.deps/Release/unix_dgram.node.d +0 -1
- package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram/src/unix_dgram.o +0 -0
- package/node_modules/unix-dgram/build/Release/obj.target/unix_dgram.node +0 -0
- package/node_modules/unix-dgram/build/Release/unix_dgram.node +0 -0
- package/node_modules/unix-dgram/build/binding.Makefile +0 -6
- package/node_modules/unix-dgram/build/config.gypi +0 -213
- package/node_modules/unix-dgram/build/unix_dgram.target.mk +0 -159
- package/node_modules/unix-dgram/lib/unix_dgram.js +0 -168
- package/node_modules/unix-dgram/package.json +0 -36
- package/node_modules/unix-dgram/src/unix_dgram.cc +0 -404
- package/node_modules/unix-dgram/src/win_dummy.cc +0 -7
- package/node_modules/unix-dgram/test/test-connect-callback.js +0 -68
- package/node_modules/unix-dgram/test/test-connect.js +0 -53
- package/node_modules/unix-dgram/test/test-dgram-unix.js +0 -58
- package/node_modules/unix-dgram/test/test-send-error.js +0 -26
- package/node_modules/winston-syslog/.eslintrc +0 -7
- package/node_modules/winston-syslog/.travis.yml +0 -14
- package/node_modules/winston-syslog/CHANGELOG.md +0 -9
- package/node_modules/winston-syslog/LICENSE +0 -20
- package/node_modules/winston-syslog/README.md +0 -135
- package/node_modules/winston-syslog/lib/utils.js +0 -26
- package/node_modules/winston-syslog/lib/winston-syslog.js +0 -385
- package/node_modules/winston-syslog/package.json +0 -56
- package/node_modules/winston-syslog/test/format-test.js +0 -122
- package/node_modules/winston-syslog/test/syslog-test.js +0 -95
- package/node_modules/winston-syslog/test/unix-connect-test.js +0 -133
|
@@ -67,6 +67,26 @@
|
|
|
67
67
|
"type": "keep"
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
|
+
"dust.escapeHtml": {
|
|
71
|
+
"enabled": true,
|
|
72
|
+
"source": "P",
|
|
73
|
+
"target": "R",
|
|
74
|
+
"tags": ["html-encoded"],
|
|
75
|
+
"type": "overload",
|
|
76
|
+
"command": {
|
|
77
|
+
"type": "keep"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"dust.escapeJs": {
|
|
81
|
+
"enabled": true,
|
|
82
|
+
"source": "P",
|
|
83
|
+
"target": "R",
|
|
84
|
+
"tags": ["javascript-encoded"],
|
|
85
|
+
"type": "overload",
|
|
86
|
+
"command": {
|
|
87
|
+
"type": "keep"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
70
90
|
"pug.compile": {
|
|
71
91
|
"enabled": true,
|
|
72
92
|
"provider": "./propagators/pug-compile.js"
|
|
@@ -233,6 +233,16 @@
|
|
|
233
233
|
"methodName": "escape",
|
|
234
234
|
"isModule": true
|
|
235
235
|
},
|
|
236
|
+
"dust.escapeHtml": {
|
|
237
|
+
"moduleName": "dustjs-linkedin",
|
|
238
|
+
"methodName": "escapeHtml",
|
|
239
|
+
"isModule": true
|
|
240
|
+
},
|
|
241
|
+
"dust.escapeJs": {
|
|
242
|
+
"moduleName": "dustjs-linkedin",
|
|
243
|
+
"methodName": "escapeJs",
|
|
244
|
+
"isModule": true
|
|
245
|
+
},
|
|
236
246
|
"express.response.send": {
|
|
237
247
|
"moduleName": "express",
|
|
238
248
|
"version": ">=4.0.0",
|
|
@@ -481,9 +481,10 @@ const agent = [
|
|
|
481
481
|
{
|
|
482
482
|
name: 'agent.stack_trace_limit',
|
|
483
483
|
arg: '<limit>',
|
|
484
|
-
default:
|
|
484
|
+
default: 25,
|
|
485
485
|
fn: parseNum,
|
|
486
|
-
desc:
|
|
486
|
+
desc:
|
|
487
|
+
'set limit for stack trace size (larger limits will improve accuracy but increase memory usage)'
|
|
487
488
|
},
|
|
488
489
|
{
|
|
489
490
|
name: 'agent.polling.app_activity_ms',
|
package/lib/core/stacktrace.js
CHANGED
|
@@ -15,11 +15,12 @@ Copyright: 2021 Contrast Security, Inc
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const semver = require('semver');
|
|
18
|
+
const agent = require('../agent');
|
|
18
19
|
const process = require('process');
|
|
19
20
|
const sourceMap = require('../util/source-map');
|
|
20
21
|
const _isAgentPath = require('../util/is-agent-path');
|
|
21
22
|
|
|
22
|
-
const STACK_TRACE_LIMIT =
|
|
23
|
+
const STACK_TRACE_LIMIT = agent.config.agent.stack_trace_limit;
|
|
23
24
|
const EVAL_ORIGIN_REGEX = /\((.*?):(\d+):\d+\)/;
|
|
24
25
|
const EVENTS_FILE = semver.gte(process.version, '16.0.0')
|
|
25
26
|
? 'node:events'
|
|
@@ -22,8 +22,14 @@ const RequestFactory = require('../../reporter/models/utils/request-factory');
|
|
|
22
22
|
const CleanStack = require('../../util/clean-stack');
|
|
23
23
|
const agentEmitter = require('../../agent-emitter');
|
|
24
24
|
|
|
25
|
+
/** @typedef {import('../../agent').ContrastAgent} Agent */
|
|
26
|
+
|
|
25
27
|
class BaseFramework {
|
|
26
|
-
|
|
28
|
+
/**
|
|
29
|
+
* @param {Object} opts
|
|
30
|
+
* @param {string} opts.id
|
|
31
|
+
* @param {Agent} opts.agent
|
|
32
|
+
*/
|
|
27
33
|
constructor({ id, agent } = {}) {
|
|
28
34
|
this.id = id;
|
|
29
35
|
this.agent = agent;
|
|
@@ -46,7 +52,7 @@ class BaseFramework {
|
|
|
46
52
|
* -- Note --
|
|
47
53
|
* The current imlementation uses a CleanStack instance in order to generate
|
|
48
54
|
* a stackframe from which to obtain the information. This can be expensive.
|
|
49
|
-
* @returns {
|
|
55
|
+
* @returns {string}
|
|
50
56
|
*/
|
|
51
57
|
getAppCodeLocation() {
|
|
52
58
|
const limit = Error.stackTraceLimit;
|
|
@@ -19,39 +19,41 @@ const moduleHook = require('../require');
|
|
|
19
19
|
const patcher = require('../patcher');
|
|
20
20
|
const { HTTP_EVENTS, PATCH_TYPES } = require('../../constants');
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
super({ id, agent });
|
|
25
|
-
|
|
26
|
-
this.init();
|
|
27
|
-
}
|
|
22
|
+
/** @typedef {import('../../agent').ContrastAgent} Agent */
|
|
23
|
+
/** @typedef {import('net').Server} Server */
|
|
28
24
|
|
|
25
|
+
class HttpFramework extends BaseFramework {
|
|
29
26
|
/**
|
|
30
|
-
*
|
|
27
|
+
* @param {Agent} agent
|
|
28
|
+
* @param {string} id
|
|
31
29
|
*/
|
|
32
|
-
|
|
30
|
+
constructor(agent, id = 'http') {
|
|
31
|
+
super({ agent, id });
|
|
33
32
|
moduleHook.resolve({ name: this.id }, this.onRequire.bind(this));
|
|
34
33
|
}
|
|
35
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @template {import('http') | import('https')} T
|
|
37
|
+
* @param {T} xport
|
|
38
|
+
* @returns {T}
|
|
39
|
+
*/
|
|
36
40
|
onRequire(xport) {
|
|
37
|
-
const { id } = this;
|
|
38
|
-
|
|
39
41
|
patcher.patch(xport, 'Server', {
|
|
40
|
-
name: `${id}.Server`,
|
|
42
|
+
name: `${this.id}.Server`,
|
|
41
43
|
patchType: PATCH_TYPES.FRAMEWORK,
|
|
42
44
|
alwaysRun: true,
|
|
43
45
|
post: (fnData) => this.handleServerCreate(fnData.args, fnData.result)
|
|
44
46
|
});
|
|
45
47
|
|
|
46
48
|
patcher.patch(xport, 'createServer', {
|
|
47
|
-
name: `${id}.createServer`,
|
|
49
|
+
name: `${this.id}.createServer`,
|
|
48
50
|
patchType: PATCH_TYPES.FRAMEWORK,
|
|
49
51
|
alwaysRun: true,
|
|
50
52
|
post: (fnData) => this.handleServerCreate(fnData.args, fnData.result)
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
patcher.patch(xport.Server.prototype, 'listen', {
|
|
54
|
-
name: `${id}.Server.prototype`,
|
|
56
|
+
name: `${this.id}.Server.prototype`,
|
|
55
57
|
patchType: PATCH_TYPES.FRAMEWORK,
|
|
56
58
|
alwaysRun: true,
|
|
57
59
|
pre: (fnData) => this.handleServerListen(fnData.args, fnData.obj)
|
|
@@ -62,6 +64,7 @@ class HttpFramework extends BaseFramework {
|
|
|
62
64
|
|
|
63
65
|
/**
|
|
64
66
|
* Emits a listen event if one has not been pubished for the server instance.
|
|
67
|
+
* @param {any[]}
|
|
65
68
|
* @param {Server} server Server on which `listen` was called
|
|
66
69
|
*/
|
|
67
70
|
handleServerListen(args, server) {
|
|
@@ -70,12 +73,16 @@ class HttpFramework extends BaseFramework {
|
|
|
70
73
|
|
|
71
74
|
/**
|
|
72
75
|
* Emits a create event for the new Server instance.
|
|
73
|
-
* @param {
|
|
76
|
+
* @param {any[]} args The arguments passed to the Server constructor
|
|
74
77
|
* @param {Server} server The http Server instance
|
|
75
78
|
*/
|
|
76
79
|
handleServerCreate(args, server) {
|
|
77
|
-
const [
|
|
78
|
-
|
|
80
|
+
const [options] = args;
|
|
81
|
+
let [, handler] = args;
|
|
82
|
+
if (typeof options === 'function') {
|
|
83
|
+
handler = options;
|
|
84
|
+
}
|
|
85
|
+
this.emitter.emit(HTTP_EVENTS.SERVER_CREATE, handler, server);
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Copyright: 2021 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 { PATCH_TYPES } = require('../../constants');
|
|
18
|
+
const patcher = require('../patcher');
|
|
19
|
+
const HttpFramework = require('./http');
|
|
20
|
+
|
|
21
|
+
/** @typedef {import('../../agent').ContrastAgent} Agent */
|
|
22
|
+
|
|
23
|
+
class Http2Framework extends HttpFramework {
|
|
24
|
+
/**
|
|
25
|
+
* @param {Agent} agent
|
|
26
|
+
*/
|
|
27
|
+
constructor(agent) {
|
|
28
|
+
super(agent, 'http2');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {import('http2')} http2
|
|
33
|
+
* @returns {import('http2')}
|
|
34
|
+
*/
|
|
35
|
+
onRequire(http2) {
|
|
36
|
+
patcher.patch(http2, 'createServer', {
|
|
37
|
+
name: `${this.id}.createServer`,
|
|
38
|
+
patchType: PATCH_TYPES.FRAMEWORK,
|
|
39
|
+
alwaysRun: true,
|
|
40
|
+
post: ({ args, result }) => this.handleServerCreate(args, result)
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
patcher.patch(http2, 'createSecureServer', {
|
|
44
|
+
name: `${this.id}.createSecureServer`,
|
|
45
|
+
patchType: PATCH_TYPES.FRAMEWORK,
|
|
46
|
+
alwaysRun: true,
|
|
47
|
+
post: ({ args, result }) => this.handleServerCreate(args, result)
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return http2;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Emits a create event for the new Server instance.
|
|
55
|
+
* @param {any[]} args The arguments passed to the Server constructor
|
|
56
|
+
* @param {import('net').Server} server The http Server instance
|
|
57
|
+
*/
|
|
58
|
+
handleServerCreate(args, server) {
|
|
59
|
+
super.handleServerCreate(args, server);
|
|
60
|
+
|
|
61
|
+
// Since the `Http2Server` class is not exported, we can patch it here after
|
|
62
|
+
// creation.
|
|
63
|
+
// NOTE: could we just patch `net.Server` here and in `http`?
|
|
64
|
+
patcher.patch(server, 'listen', {
|
|
65
|
+
name: `${this.id}.Http2Server.listen`,
|
|
66
|
+
patchType: PATCH_TYPES.FRAMEWORK,
|
|
67
|
+
alwaysRun: true,
|
|
68
|
+
pre: ({ args, obj }) => this.handleServerListen(args, obj)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = Http2Framework;
|
|
@@ -14,9 +14,14 @@ Copyright: 2021 Contrast Security, Inc
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
+
const Http = require('./http');
|
|
18
|
+
const Http2 = require('./http2');
|
|
19
|
+
const Hapi16 = require('./hapi16');
|
|
20
|
+
|
|
17
21
|
module.exports = function(agent) {
|
|
18
22
|
// load all the hooks
|
|
19
|
-
new (
|
|
20
|
-
new (
|
|
21
|
-
new (
|
|
23
|
+
new Http(agent);
|
|
24
|
+
new Http(agent, 'https');
|
|
25
|
+
new Http2(agent);
|
|
26
|
+
new Hapi16(agent);
|
|
22
27
|
};
|
package/lib/hooks/http.js
CHANGED
|
@@ -35,39 +35,45 @@ const logger = require('../core/logger')('contrast:hooks');
|
|
|
35
35
|
const patcher = require('./patcher');
|
|
36
36
|
const { PATCH_TYPES } = require('../constants');
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
logger.info('applying non-policy hook: http');
|
|
38
|
+
const flatten = (ary) => Array.prototype.join.apply(ary, [',']);
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Logs stack for every res.[end, writeHead, write] call
|
|
42
|
+
*
|
|
43
|
+
* @param {ServerResponse} response
|
|
44
|
+
* @param {string} method method to hook
|
|
45
|
+
*/
|
|
46
|
+
const logHook = (response, method) => {
|
|
47
|
+
patcher.patch(response, method, {
|
|
48
|
+
alwaysRun: true,
|
|
49
|
+
name: 'req-log',
|
|
50
|
+
patchType: PATCH_TYPES.MISC,
|
|
51
|
+
post(data) {
|
|
52
|
+
const { stack } = new Error();
|
|
53
|
+
logger.info('res.%s(%s):%o', method, flatten(data.args), stack);
|
|
54
|
+
}
|
|
46
55
|
});
|
|
47
56
|
};
|
|
48
57
|
|
|
49
|
-
module.exports.getId = getId;
|
|
50
|
-
module.exports.hookServerPrototype = hookServerPrototype;
|
|
51
|
-
|
|
52
58
|
/**
|
|
53
59
|
* Hooks the Server prototype's <code>emit</code> method.
|
|
54
|
-
* @param {Server}
|
|
60
|
+
* @param {Server} server The Server prototype
|
|
55
61
|
*/
|
|
56
|
-
|
|
57
|
-
patcher.patch(
|
|
62
|
+
const hookServer = (server, agent, id) => {
|
|
63
|
+
patcher.patch(server, 'listen', {
|
|
58
64
|
alwaysRun: true,
|
|
59
65
|
name: 'server-listen-log-time',
|
|
60
66
|
patchType: PATCH_TYPES.MISC,
|
|
61
|
-
pre(
|
|
67
|
+
pre() {
|
|
62
68
|
logger.info(`${process.pid}: http listen after ${process.uptime()}`);
|
|
63
69
|
}
|
|
64
70
|
});
|
|
65
71
|
|
|
66
72
|
// Wrap the request emit in a contrast domain to track non-dataflow hooks, and for storage.
|
|
67
|
-
const { emit } =
|
|
68
|
-
|
|
69
|
-
const [event, req, res] = args;
|
|
73
|
+
const { emit } = server;
|
|
74
|
+
server.emit = function newEmit(...args) {
|
|
70
75
|
const self = this;
|
|
76
|
+
const [event, req, res] = args;
|
|
71
77
|
|
|
72
78
|
if (event === 'request') {
|
|
73
79
|
logger.debug('Received request %s, method %s', req.url, req.method);
|
|
@@ -75,35 +81,77 @@ function hookServerPrototype(proto, agent) {
|
|
|
75
81
|
agent,
|
|
76
82
|
req,
|
|
77
83
|
res,
|
|
78
|
-
hookResponse,
|
|
79
|
-
|
|
84
|
+
hookResponse(response, agent) {
|
|
85
|
+
const { writeHead, end } = response;
|
|
86
|
+
// XXX(ehden): we keep the original method available to call when handling
|
|
87
|
+
// blocked requests because of MethodTampering or other rules whose sink
|
|
88
|
+
// type is RESPONSE_STATUS and which can block at perimeter. We call the original
|
|
89
|
+
// when we block to prevent recursing through blocks by setting our own 403.
|
|
90
|
+
response[WRITE_HEAD] = writeHead;
|
|
91
|
+
response[END] = end;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Patch writeHead to emit a sink event before calling writeHead
|
|
95
|
+
*
|
|
96
|
+
*/
|
|
97
|
+
patcher.patch(response, 'writeHead', {
|
|
98
|
+
alwaysRun: true,
|
|
99
|
+
name: 'write-head',
|
|
100
|
+
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
101
|
+
pre(data) {
|
|
102
|
+
const [statusCode, reason] = data.args;
|
|
103
|
+
let [, , obj] = data.args;
|
|
104
|
+
let contentType;
|
|
105
|
+
if (typeof reason !== 'string') {
|
|
106
|
+
obj = reason;
|
|
107
|
+
}
|
|
108
|
+
if (obj && typeof obj === 'object') {
|
|
109
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
110
|
+
if (key.toLowerCase() === 'content-type') {
|
|
111
|
+
contentType = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (contentType) {
|
|
116
|
+
AsyncStorage.set(KEYS.RESPONSE_CONTENT_TYPE, contentType);
|
|
117
|
+
}
|
|
118
|
+
emitSinkEvent(statusCode, SINK_TYPES.RESPONSE_STATUS, id, false);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
patcher.patch(response, 'setHeader', {
|
|
123
|
+
alwaysRun: true,
|
|
124
|
+
name: 'set-header',
|
|
125
|
+
patchType: PATCH_TYPES.ASYNC_CONTEXT,
|
|
126
|
+
pre(data) {
|
|
127
|
+
const [name = '', value] = data.args;
|
|
128
|
+
if (name.toLowerCase() === 'content-type' && value) {
|
|
129
|
+
AsyncStorage.set(KEYS.RESPONSE_CONTENT_TYPE, value);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// special HTTP logging for responses.
|
|
135
|
+
if (agent.config.agent.logger.log_outbound_http) {
|
|
136
|
+
['end', 'writeHead', 'write'].forEach((method) => {
|
|
137
|
+
logHook(response, method);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
callback() {
|
|
80
142
|
const ipEvent = createSourceEvent(
|
|
81
143
|
req,
|
|
82
144
|
req,
|
|
83
145
|
res,
|
|
84
|
-
'
|
|
146
|
+
'socket.remoteAddress',
|
|
85
147
|
INPUT_TYPES.IP,
|
|
86
|
-
|
|
148
|
+
id
|
|
87
149
|
);
|
|
88
150
|
agentEmitter.emit('http.requestStart', req, res, ipEvent);
|
|
89
151
|
|
|
90
|
-
emitSourceEvent(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
res,
|
|
94
|
-
'method',
|
|
95
|
-
INPUT_TYPES.METHOD,
|
|
96
|
-
getId(req)
|
|
97
|
-
);
|
|
98
|
-
emitSourceEvent(
|
|
99
|
-
req,
|
|
100
|
-
req,
|
|
101
|
-
res,
|
|
102
|
-
'headers',
|
|
103
|
-
INPUT_TYPES.HEADER,
|
|
104
|
-
getId(req)
|
|
105
|
-
);
|
|
106
|
-
emitSourceEvent(req, req, res, 'url', INPUT_TYPES.URL, getId(req));
|
|
152
|
+
emitSourceEvent(req, req, res, 'method', INPUT_TYPES.METHOD, id);
|
|
153
|
+
emitSourceEvent(req, req, res, 'headers', INPUT_TYPES.HEADER, id);
|
|
154
|
+
emitSourceEvent(req, req, res, 'url', INPUT_TYPES.URL, id);
|
|
107
155
|
|
|
108
156
|
const rv = emit.apply(self, args);
|
|
109
157
|
|
|
@@ -115,101 +163,37 @@ function hookServerPrototype(proto, agent) {
|
|
|
115
163
|
return emit.apply(self, args);
|
|
116
164
|
}
|
|
117
165
|
};
|
|
118
|
-
}
|
|
166
|
+
};
|
|
119
167
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
* context.
|
|
123
|
-
* @param {ServerResponse} res
|
|
124
|
-
*/
|
|
125
|
-
function hookResponse(res, agent) {
|
|
126
|
-
const flattenArgs = (args) => Array.prototype.join.apply(args, [',']);
|
|
127
|
-
|
|
128
|
-
/*
|
|
129
|
-
* Logs stack for every res.[end, writeHead, write] call
|
|
130
|
-
*
|
|
131
|
-
* @param {ServerResponse} res
|
|
132
|
-
* @param {string} method method to hook
|
|
133
|
-
*/
|
|
134
|
-
function logHook(res, method) {
|
|
135
|
-
patcher.patch(res, method, {
|
|
136
|
-
alwaysRun: true,
|
|
137
|
-
name: 'req-log',
|
|
138
|
-
patchType: PATCH_TYPES.MISC,
|
|
139
|
-
post(data) {
|
|
140
|
-
const { stack } = new Error();
|
|
141
|
-
logger.info('res.%s(%s):%o', method, flattenArgs(data.args), stack);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const { writeHead, end } = res;
|
|
147
|
-
// XXX(ehden): we keep the original method available to call when handling
|
|
148
|
-
// blocked requests because of MethodTampering or other rules whose sink
|
|
149
|
-
// type is RESPONSE_STATUS and which can block at perimeter. We call the original
|
|
150
|
-
// when we block to prevent recursing through blocks by setting our own 403.
|
|
151
|
-
res[WRITE_HEAD] = writeHead;
|
|
152
|
-
res[END] = end;
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Patch writeHead to emit a sink event before calling writeHead
|
|
156
|
-
*
|
|
157
|
-
*/
|
|
158
|
-
patcher.patch(res, 'writeHead', {
|
|
159
|
-
alwaysRun: true,
|
|
160
|
-
name: 'write-head',
|
|
161
|
-
patchType: PATCH_TYPES.PROTECT_SINK,
|
|
162
|
-
pre(data) {
|
|
163
|
-
let contentType;
|
|
164
|
-
if (data.args[1] && typeof data.args[1] === 'object') {
|
|
165
|
-
for (const [key, value] of Object.entries(data.args[1])) {
|
|
166
|
-
if (key.toLowerCase() === 'content-type') {
|
|
167
|
-
contentType = value;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
168
|
+
module.exports = (agent) => {
|
|
169
|
+
logger.info('applying non-policy hook: http');
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
const [statusCode] = data.args;
|
|
176
|
-
emitSinkEvent(statusCode, SINK_TYPES.RESPONSE_STATUS, getId(this), false);
|
|
177
|
-
}
|
|
171
|
+
moduleHook.resolve({ name: 'http' }, (http) => {
|
|
172
|
+
hookServer(http.Server.prototype, agent, 'http');
|
|
178
173
|
});
|
|
179
174
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
name: 'set-header',
|
|
183
|
-
patchType: PATCH_TYPES.ASYNC_CONTEXT,
|
|
184
|
-
pre(data) {
|
|
185
|
-
if (
|
|
186
|
-
(data.args[0] || '').toLowerCase() === 'content-type' &&
|
|
187
|
-
data.args[1]
|
|
188
|
-
) {
|
|
189
|
-
AsyncStorage.set(KEYS.RESPONSE_CONTENT_TYPE, data.args[1]);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
175
|
+
moduleHook.resolve({ name: 'https' }, (https) => {
|
|
176
|
+
hookServer(https.Server.prototype, agent, 'https');
|
|
192
177
|
});
|
|
193
178
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
179
|
+
moduleHook.resolve({ name: 'http2' }, (http2) => {
|
|
180
|
+
patcher.patch(http2, 'createServer', {
|
|
181
|
+
name: `create-server-http2-hooks`,
|
|
182
|
+
patchType: PATCH_TYPES.MISC,
|
|
183
|
+
alwaysRun: true,
|
|
184
|
+
post({ result }) {
|
|
185
|
+
hookServer(result, agent);
|
|
186
|
+
}
|
|
198
187
|
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
188
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return req.socket.encrypted ? 'https' : 'http';
|
|
214
|
-
}
|
|
215
|
-
}
|
|
189
|
+
patcher.patch(http2, 'createSecureServer', {
|
|
190
|
+
name: `create-secure-server-http2-hooks`,
|
|
191
|
+
patchType: PATCH_TYPES.MISC,
|
|
192
|
+
alwaysRun: true,
|
|
193
|
+
post({ result }) {
|
|
194
|
+
hookServer(result, agent);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
module.exports.hookServer = hookServer;
|
package/lib/hooks/require.js
CHANGED
|
@@ -14,44 +14,38 @@ Copyright: 2021 Contrast Security, Inc
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* @module lib/hooks/moduleHook
|
|
19
|
-
* @class ModuleHook
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
17
|
const Module = require('module');
|
|
23
|
-
const agentEmitter = require('../agent-emitter');
|
|
24
18
|
const RequireHook = require('@contrast/require-hook');
|
|
19
|
+
const agentEmitter = require('../agent-emitter');
|
|
25
20
|
const logger = require('../core/logger')('hooks:require');
|
|
26
21
|
|
|
27
22
|
class ModuleHook {
|
|
28
|
-
constructor(
|
|
23
|
+
constructor() {
|
|
29
24
|
this.reqHook = new RequireHook(logger);
|
|
30
25
|
|
|
31
|
-
// RequireHook takes care of hooking
|
|
32
|
-
//
|
|
33
|
-
const
|
|
34
|
-
Module.
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
// RequireHook takes care of hooking but we still need to patch require to
|
|
27
|
+
// emit 'require' events
|
|
28
|
+
const origModLoad = Module._load;
|
|
29
|
+
Module._load = function __loadAndEmit(...args) {
|
|
30
|
+
const [request] = args;
|
|
31
|
+
agentEmitter.emit('require', request);
|
|
32
|
+
return Reflect.apply(origModLoad, this, args);
|
|
37
33
|
};
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
/**
|
|
41
|
-
*
|
|
42
37
|
* Collects instrumentation functions that will run on the specified
|
|
43
|
-
* module's file at the time it is loaded via
|
|
44
|
-
* @param {
|
|
45
|
-
* @param {Function}
|
|
46
|
-
* the time it is required in the code
|
|
38
|
+
* module's file at the time it is loaded via `require`.
|
|
39
|
+
* @param {RequireHook.Descriptor} descriptor describes the module to hook
|
|
40
|
+
* @param {Function} handler The function to run on the module at the time it is required
|
|
47
41
|
*/
|
|
48
|
-
resolve(descriptor,
|
|
49
|
-
this.reqHook.resolve(descriptor,
|
|
42
|
+
resolve(descriptor, handler) {
|
|
43
|
+
this.reqHook.resolve(descriptor, handler);
|
|
50
44
|
}
|
|
51
45
|
|
|
52
46
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
47
|
+
* Reset the hooked state for a module so that it's handlers re-run.
|
|
48
|
+
* NOTE: used in unit tests.
|
|
55
49
|
* @param {string} name the name of the module
|
|
56
50
|
*/
|
|
57
51
|
reset(name) {
|
package/lib/instrumentation.js
CHANGED
|
@@ -40,9 +40,6 @@ module.exports = function instrument(agent, reporter) {
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const stackFactory = require('./core/stacktrace').singleton;
|
|
44
|
-
stackFactory.stackTraceLimit = agent.config.agent.stack_trace_limit;
|
|
45
|
-
|
|
46
43
|
// The order of these matters, do not re-order
|
|
47
44
|
collectLibInfo(agent);
|
|
48
45
|
// (CONTRAST-31526) this must be required first to load global.ContrastMethods
|