@contrast/agentify 1.2.0 → 1.3.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/lib/index.js +50 -16
- package/lib/sources.js +51 -58
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
18
20
|
const Module = require('module');
|
|
19
21
|
|
|
20
22
|
const defaultOpts = {
|
|
@@ -25,20 +27,22 @@ const defaultOpts = {
|
|
|
25
27
|
'deadzones',
|
|
26
28
|
'scopes',
|
|
27
29
|
'sources',
|
|
30
|
+
'architectureComponents',
|
|
28
31
|
'assess',
|
|
29
32
|
'protect',
|
|
30
33
|
'depHooks',
|
|
34
|
+
'enhancedLibraryUsage',
|
|
31
35
|
'rewriteHooks',
|
|
32
|
-
'functionHooks'
|
|
36
|
+
'functionHooks'
|
|
33
37
|
]
|
|
34
38
|
};
|
|
35
39
|
|
|
36
|
-
module.exports = function(
|
|
40
|
+
module.exports = function(core) {
|
|
37
41
|
// compose add'l local services
|
|
38
|
-
require('./sources')(
|
|
39
|
-
require('./contrast-methods')(
|
|
40
|
-
require('./function-hooks')(
|
|
41
|
-
require('./rewrite-hooks')(
|
|
42
|
+
require('./sources')(core);
|
|
43
|
+
require('./contrast-methods')(core);
|
|
44
|
+
require('./function-hooks')(core);
|
|
45
|
+
require('./rewrite-hooks')(core);
|
|
42
46
|
|
|
43
47
|
/**
|
|
44
48
|
* The interface is a function, which when called, will hook runMain
|
|
@@ -56,10 +60,10 @@ module.exports = function(deps) {
|
|
|
56
60
|
opts = { ...defaultOpts, ...opts };
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
return new Agent(
|
|
63
|
+
return new Agent(core, opts);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
core.agentify = agentify;
|
|
63
67
|
|
|
64
68
|
return agentify;
|
|
65
69
|
};
|
|
@@ -67,16 +71,16 @@ module.exports = function(deps) {
|
|
|
67
71
|
class Agent {
|
|
68
72
|
/**
|
|
69
73
|
*
|
|
70
|
-
* @param {object}
|
|
74
|
+
* @param {object} core dependencies
|
|
71
75
|
* @param {object} opts
|
|
72
76
|
* @param {function} opts.preRunMain custom function for registering services
|
|
73
77
|
* @param {boolean} opts.install whether to automatically install
|
|
74
78
|
*/
|
|
75
|
-
constructor(
|
|
79
|
+
constructor(core, opts) {
|
|
76
80
|
const self = this;
|
|
77
|
-
const { config, logger } =
|
|
81
|
+
const { config, logger } = core;
|
|
78
82
|
|
|
79
|
-
this.
|
|
83
|
+
this.core = core;
|
|
80
84
|
this.opts = opts;
|
|
81
85
|
this.runMain = Module.runMain;
|
|
82
86
|
|
|
@@ -89,7 +93,7 @@ class Agent {
|
|
|
89
93
|
logger.info('Starting the Contrast agent');
|
|
90
94
|
logger.debug({ config }, 'Agent configuration');
|
|
91
95
|
|
|
92
|
-
const plugin = await opts.preRunMain(
|
|
96
|
+
const plugin = await opts.preRunMain(core);
|
|
93
97
|
|
|
94
98
|
if (opts.install) {
|
|
95
99
|
await self.install();
|
|
@@ -101,6 +105,8 @@ class Agent {
|
|
|
101
105
|
} catch (err) {
|
|
102
106
|
await self.handleInstallFailure(err);
|
|
103
107
|
}
|
|
108
|
+
|
|
109
|
+
self.logEffectiveConfig();
|
|
104
110
|
return self.runMain.apply(this, args);
|
|
105
111
|
};
|
|
106
112
|
}
|
|
@@ -113,7 +119,7 @@ class Agent {
|
|
|
113
119
|
* @param {error} err Error thrown during install
|
|
114
120
|
*/
|
|
115
121
|
async handleInstallFailure(err) {
|
|
116
|
-
this.
|
|
122
|
+
this.core.logger.error(
|
|
117
123
|
{ err },
|
|
118
124
|
'A fatal agent installation error has occurred. The application will be run without instrumentation.'
|
|
119
125
|
);
|
|
@@ -121,13 +127,41 @@ class Agent {
|
|
|
121
127
|
|
|
122
128
|
async install() {
|
|
123
129
|
for (const svcName of this.opts.svcList) {
|
|
124
|
-
|
|
125
|
-
const svc = this.deps[svcName];
|
|
130
|
+
const svc = this.core[svcName];
|
|
126
131
|
if (svc && svc.install) {
|
|
132
|
+
this.core.logger.trace('installing service: %s', svcName);
|
|
127
133
|
await svc.install();
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
136
|
}
|
|
137
|
+
|
|
138
|
+
logEffectiveConfig() {
|
|
139
|
+
const { config, getEffectiveConfig } = this.core;
|
|
140
|
+
|
|
141
|
+
if (config._flat['agent.diagnostics.enable'] !== false) {
|
|
142
|
+
const content = JSON.stringify(getEffectiveConfig(), null, 2).concat('\n\n');
|
|
143
|
+
|
|
144
|
+
if (config._flat['agent.diagnostics.quiet'] !== true) {
|
|
145
|
+
fs.writeFileSync(1, content, 'utf8');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let outputDir = config._flat['agent.diagnostics.report_path'];
|
|
149
|
+
if (!outputDir && config.agent.logger.path) {
|
|
150
|
+
outputDir = path.join(config.agent.logger.path, '../contrast_effective_config.json');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
fs.writeFileSync(outputDir, content, 'utf-8');
|
|
155
|
+
} catch (err) {
|
|
156
|
+
outputDir = path.join(process.cwd(), 'contrast_effective_config.json');
|
|
157
|
+
try {
|
|
158
|
+
fs.writeFileSync(outputDir, content, 'utf-8');
|
|
159
|
+
} catch (err) {
|
|
160
|
+
fs.writeFileSync(1, `Couldn't create effective config file: ${err}`, 'utf-8');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
131
165
|
}
|
|
132
166
|
|
|
133
167
|
module.exports.Agent = Agent;
|
package/lib/sources.js
CHANGED
|
@@ -14,73 +14,66 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
|
+
const patchType = 'http-sources';
|
|
17
18
|
|
|
18
19
|
module.exports = function(core) {
|
|
19
|
-
const {
|
|
20
|
-
|
|
20
|
+
const {
|
|
21
|
+
instrumentation: { instrument },
|
|
22
|
+
scopes: { sources: sourcesStorage }
|
|
23
|
+
} = core;
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
depHooks.resolve({ name: 'https' }, https => patchServerEmit(https, 'httpsServer'));
|
|
25
|
-
depHooks.resolve({ name: 'http2' }, http2 => {
|
|
26
|
-
patchCreateServer(http2, 'http2Server');
|
|
27
|
-
patchCreateServer(http2, 'http2SecureServer', 'createSecureServer');
|
|
28
|
-
});
|
|
29
|
-
depHooks.resolve({ name: 'spdy' }, spdy => patchServerEmit(spdy, 'spdyServer'));
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
function patchServerEmit(serverSource, sourceName) {
|
|
33
|
-
serverSource.Server.prototype = patcher.patch(serverSource.Server.prototype, 'emit', {
|
|
34
|
-
name: `${sourceName}.Server.prototype.emit`,
|
|
35
|
-
patchType: 'http-sources',
|
|
36
|
-
around: emitAroundHook(sourceName)
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function patchCreateServer(serverSource, sourceName, constructorName = 'createServer') {
|
|
41
|
-
patcher.patch(serverSource, constructorName, {
|
|
42
|
-
name: sourceName,
|
|
43
|
-
patchType: 'http-sources',
|
|
44
|
-
post: createServerPostHook(sourceName)
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function createServerPostHook(serverType) {
|
|
49
|
-
return function(data) {
|
|
50
|
-
const { result: server } = data;
|
|
51
|
-
const serverPrototype = server ? Object.getPrototypeOf(server) : null;
|
|
52
|
-
|
|
53
|
-
if (!serverPrototype) {
|
|
54
|
-
logger.error('Unable to patch server prototype, continue without instrumentation');
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
patcher.patch(serverPrototype, 'emit', {
|
|
59
|
-
name: 'Server.prototype.emit',
|
|
60
|
-
patchType: 'req-async-storage',
|
|
61
|
-
around: emitAroundHook(serverType)
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function emitAroundHook(serverType) {
|
|
67
|
-
return function emitAroundHook(next, data) {
|
|
25
|
+
function aroundHook(serverType) {
|
|
26
|
+
return function around(next, data) {
|
|
68
27
|
const { args: [event] } = data;
|
|
69
|
-
|
|
70
28
|
if (event !== 'request') return next();
|
|
71
|
-
|
|
72
29
|
const store = { serverType };
|
|
73
|
-
|
|
74
30
|
return sourcesStorage.run(store, next);
|
|
75
31
|
};
|
|
76
32
|
}
|
|
77
33
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
34
|
+
function install() {
|
|
35
|
+
[{
|
|
36
|
+
moduleName: 'http'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
moduleName: 'https'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
moduleName: 'spdy'
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
moduleName: 'http2',
|
|
46
|
+
patchObjectsProps: [
|
|
47
|
+
{
|
|
48
|
+
methods: ['createServer', 'createSecureServer'],
|
|
49
|
+
patchType,
|
|
50
|
+
patchObjects: [
|
|
51
|
+
{
|
|
52
|
+
name: 'Server.prototype',
|
|
53
|
+
methods: ['emit'],
|
|
54
|
+
patchType,
|
|
55
|
+
around: aroundHook('http2')
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}].forEach(({ moduleName, patchObjectsProps }) => {
|
|
61
|
+
const patchObjects = patchObjectsProps || [
|
|
62
|
+
{
|
|
63
|
+
name: 'Server.prototype',
|
|
64
|
+
methods: ['emit'],
|
|
65
|
+
patchType,
|
|
66
|
+
around: aroundHook(moduleName)
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
instrument({
|
|
70
|
+
moduleName,
|
|
71
|
+
patchObjects
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return core.sources = {
|
|
76
|
+
install,
|
|
77
|
+
aroundHook
|
|
85
78
|
};
|
|
86
79
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agentify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Configures Contrast agent services and instrumentation within an application",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -16,4 +16,4 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"test": "../scripts/test.sh"
|
|
18
18
|
}
|
|
19
|
-
}
|
|
19
|
+
}
|