@contrast/agentify 1.20.1 → 1.22.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/function-hooks.js +1 -1
- package/lib/index.d.ts +32 -13
- package/lib/index.js +85 -61
- package/lib/rewrite-hooks.js +33 -9
- package/package.json +10 -10
- package/lib/initialize.mjs +0 -156
package/lib/function-hooks.js
CHANGED
|
@@ -78,7 +78,7 @@ module.exports = function (deps) {
|
|
|
78
78
|
// cannot parse lone function expressions that are not part of an assignment.
|
|
79
79
|
try {
|
|
80
80
|
let unwritten = functionHooks.contextualizeFunction(code);
|
|
81
|
-
unwritten = rewriter.
|
|
81
|
+
unwritten = rewriter.unwriteSync(unwritten);
|
|
82
82
|
unwritten = unwritten.replace(METHOD_CONTEXT, '');
|
|
83
83
|
unwritten = unwritten.replace(FUNCTION_CONTEXT, '');
|
|
84
84
|
unwritten = unwritten.replace(/;\s*$/, ''); // removes trailing semicolon/whitespace
|
package/lib/index.d.ts
CHANGED
|
@@ -13,21 +13,36 @@
|
|
|
13
13
|
* way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { Config } from '@contrast/config';
|
|
17
16
|
import { Installable } from '@contrast/common';
|
|
17
|
+
import { Config } from '@contrast/config';
|
|
18
|
+
import { Core as _Core } from '@contrast/core';
|
|
18
19
|
import { Logger } from '@contrast/logger';
|
|
19
20
|
import { Rewriter } from '@contrast/rewriter';
|
|
20
21
|
import RequireHook from '@contrast/require-hook';
|
|
22
|
+
import { Patcher } from '@contrast/patcher';
|
|
23
|
+
import { Scopes } from '@contrast/scopes';
|
|
24
|
+
import { Deadzones } from '@contrast/deadzones';
|
|
25
|
+
import { ReporterBus } from '@contrast/reporter';
|
|
21
26
|
|
|
22
27
|
declare module 'module' {
|
|
23
28
|
class Module {
|
|
24
29
|
/**
|
|
30
|
+
* @see https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js
|
|
31
|
+
* @param content The source code of the module
|
|
32
|
+
* @param filename The file path of the module
|
|
33
|
+
*/
|
|
34
|
+
_compile(content: string, filename: string);
|
|
35
|
+
|
|
36
|
+
static _extensions: {
|
|
37
|
+
/**
|
|
25
38
|
* @see https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js
|
|
26
|
-
* @param
|
|
39
|
+
* @param module The module to compile
|
|
27
40
|
* @param filename The file path of the module
|
|
28
41
|
*/
|
|
29
|
-
|
|
42
|
+
['.js'](module: import('module'), filename: string);
|
|
43
|
+
};
|
|
30
44
|
}
|
|
45
|
+
|
|
31
46
|
export = Module;
|
|
32
47
|
}
|
|
33
48
|
|
|
@@ -36,13 +51,17 @@ declare module 'node:module' {
|
|
|
36
51
|
export = Module;
|
|
37
52
|
}
|
|
38
53
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
export interface Core extends _Core {
|
|
55
|
+
config: Config;
|
|
56
|
+
logger: Logger;
|
|
57
|
+
depHooks: RequireHook;
|
|
58
|
+
patcher: Patcher
|
|
59
|
+
rewriter: Rewriter;
|
|
60
|
+
scopes: Scopes;
|
|
61
|
+
deadzones: Deadzones;
|
|
62
|
+
reporter: ReporterBus;
|
|
63
|
+
instrumentation: any;
|
|
64
|
+
metrics: any;
|
|
46
65
|
}
|
|
47
66
|
|
|
48
67
|
export interface AgentifyOptions {
|
|
@@ -50,13 +69,13 @@ export interface AgentifyOptions {
|
|
|
50
69
|
}
|
|
51
70
|
|
|
52
71
|
export interface PreRunMain {
|
|
53
|
-
(core:
|
|
72
|
+
(core: Core): Installable | void | Promise<Installable | void>;
|
|
54
73
|
}
|
|
55
74
|
|
|
56
75
|
export interface Agentify {
|
|
57
|
-
(preRunMain: PreRunMain, opts?: AgentifyOptions):
|
|
76
|
+
(preRunMain: PreRunMain, opts?: AgentifyOptions): Installable | void;
|
|
58
77
|
}
|
|
59
78
|
|
|
60
|
-
declare function init(core:
|
|
79
|
+
declare function init(core: Partial<Core>): Agentify;
|
|
61
80
|
|
|
62
81
|
export = init;
|
package/lib/index.js
CHANGED
|
@@ -19,6 +19,14 @@ const Module = require('module');
|
|
|
19
19
|
const { IntentionalError } = require('@contrast/common');
|
|
20
20
|
|
|
21
21
|
const ERROR_MESSAGE = 'An error prevented the Contrast agent from installing. The application will be run without instrumentation.';
|
|
22
|
+
/**
|
|
23
|
+
* Specific agents may want to add their startup validation to this list as needed.
|
|
24
|
+
* We have to install the reporter first since installation is contingent on TS onboarding responses.
|
|
25
|
+
* Otherwise, the order of installing side effects doesn't matter too much. Ideally, the installations
|
|
26
|
+
* that most affect the environment and "normal" execution of things should be done lastly e.g. modifying prototypes.
|
|
27
|
+
* A good rule might be to install in the order such that side-effects that are easier to undo should be done first.
|
|
28
|
+
* With that rule in mind, ESM support is the most complex (loader agent) so it should come last.
|
|
29
|
+
*/
|
|
22
30
|
const DEFAULT_INSTALL_ORDER = [
|
|
23
31
|
'reporter',
|
|
24
32
|
'contrastMethods',
|
|
@@ -32,16 +40,89 @@ const DEFAULT_INSTALL_ORDER = [
|
|
|
32
40
|
'routeCoverage',
|
|
33
41
|
'libraryAnalysis',
|
|
34
42
|
'heapSnapshots',
|
|
43
|
+
'metrics',
|
|
35
44
|
'rewriteHooks',
|
|
36
45
|
'functionHooks',
|
|
37
|
-
'
|
|
46
|
+
'esmHooks',
|
|
38
47
|
];
|
|
39
48
|
|
|
40
49
|
/**
|
|
50
|
+
* Utility for making agents: entrypoints indended to be run via --require, --loader, --import.
|
|
51
|
+
* Composes all needed dependencies during factory instantiation, and installs various components
|
|
52
|
+
* that alter an application's environment in order for composed features to operate e.g. request scopes,
|
|
53
|
+
* source code rewrite hooks etc.
|
|
41
54
|
* @param {any} core
|
|
42
55
|
* @returns {import('.').Agentify}
|
|
43
56
|
*/
|
|
44
57
|
module.exports = function init(core = {}) {
|
|
58
|
+
core.startTime = process.hrtime.bigint();
|
|
59
|
+
|
|
60
|
+
let _callback;
|
|
61
|
+
let _opts;
|
|
62
|
+
|
|
63
|
+
core.agentify = async function agentify(callback, opts = {}) {
|
|
64
|
+
_callback = callback;
|
|
65
|
+
_opts = opts;
|
|
66
|
+
_opts.type = _opts.type ?? 'cjs';
|
|
67
|
+
_opts.installOrder = _opts.installOrder ?? DEFAULT_INSTALL_ORDER;
|
|
68
|
+
|
|
69
|
+
// for 'cjs' we hook runMain and install prior to running it
|
|
70
|
+
if (_opts.type === 'cjs') {
|
|
71
|
+
if (typeof callback !== 'function') {
|
|
72
|
+
throw new Error('Invalid usage: first argument must be a function');
|
|
73
|
+
}
|
|
74
|
+
const { runMain } = Module;
|
|
75
|
+
/**
|
|
76
|
+
* This is one side effect that will always occur, even with `installOrder: []`.
|
|
77
|
+
* The act of calling `agentify()` is enough to cause this side effect, by design.
|
|
78
|
+
*/
|
|
79
|
+
Module.runMain = async function (...args) {
|
|
80
|
+
await install();
|
|
81
|
+
return runMain.apply(this, args);
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
// for 'esm' we load esm-hooks support, which will install lastly
|
|
85
|
+
const { default: esmHooks } = await import('@contrast/esm-hooks/lib/index.mjs');
|
|
86
|
+
esmHooks(core);
|
|
87
|
+
|
|
88
|
+
await install();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return core;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
async function install() {
|
|
95
|
+
const { config, logger } = core;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
for (const err of config._errors) {
|
|
99
|
+
throw err; // move this into config itself since we now handle errors
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
logger.info('Starting %s v%s', core.agentName, core.agentVersion);
|
|
103
|
+
logger.info({ config }, 'Agent configuration');
|
|
104
|
+
|
|
105
|
+
const plugin = await _callback?.(core);
|
|
106
|
+
|
|
107
|
+
for (const svcName of _opts.installOrder ?? []) {
|
|
108
|
+
const svc = core[svcName];
|
|
109
|
+
if (svc?.install) {
|
|
110
|
+
logger.trace('installing service: %s', svcName);
|
|
111
|
+
await svc.install();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (plugin?.install) {
|
|
116
|
+
await plugin.install();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
core.logDiagnosticFiles(); // should this be moved into a separate install side-effect?
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent
|
|
122
|
+
logger.error({ err }, ERROR_MESSAGE);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
45
126
|
try {
|
|
46
127
|
require('@contrast/core/lib/messages')(core);
|
|
47
128
|
require('@contrast/config')(core);
|
|
@@ -49,8 +130,6 @@ module.exports = function init(core = {}) {
|
|
|
49
130
|
throw core.config._errors[0];
|
|
50
131
|
}
|
|
51
132
|
require('@contrast/logger').default(core);
|
|
52
|
-
|
|
53
|
-
// @contrast/info ?
|
|
54
133
|
require('@contrast/core/lib/agent-info')(core);
|
|
55
134
|
require('@contrast/core/lib/system-info')(core);
|
|
56
135
|
require('@contrast/core/lib/app-info')(core);
|
|
@@ -62,77 +141,20 @@ module.exports = function init(core = {}) {
|
|
|
62
141
|
require('@contrast/dep-hooks')(core);
|
|
63
142
|
require('@contrast/patcher')(core);
|
|
64
143
|
require('@contrast/core/lib/capture-stacktrace')(core);
|
|
65
|
-
|
|
66
144
|
require('@contrast/rewriter')(core); // merge contrast-methods?
|
|
67
145
|
require('@contrast/core/lib/contrast-methods')(core); // can we remove dependency on patcher?
|
|
68
|
-
|
|
69
146
|
require('@contrast/scopes')(core);
|
|
70
147
|
require('@contrast/deadzones')(core);
|
|
71
148
|
require('@contrast/reporter').default(core);
|
|
72
149
|
require('@contrast/instrumentation')(core);
|
|
73
150
|
require('@contrast/metrics')(core);
|
|
74
151
|
|
|
75
|
-
// compose
|
|
152
|
+
// compose additional local services
|
|
76
153
|
require('./heap-snapshots')(core);
|
|
77
154
|
require('./sources')(core);
|
|
78
155
|
require('./function-hooks')(core);
|
|
79
156
|
require('./log-diagnostic-files')(core); // this doesn't really belong in agentify
|
|
80
157
|
require('./rewrite-hooks')(core);
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The interface is a function, which when called, will hook runMain
|
|
84
|
-
* @type {import('.').Agentify}
|
|
85
|
-
*/
|
|
86
|
-
core.agentify = function agentify(
|
|
87
|
-
preRunMain,
|
|
88
|
-
{ installOrder = DEFAULT_INSTALL_ORDER } = {},
|
|
89
|
-
) {
|
|
90
|
-
if (typeof preRunMain !== 'function') {
|
|
91
|
-
throw new Error('Invalid usage: first argument must be a function');
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const { runMain } = Module;
|
|
95
|
-
const { config, logger } = core;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* This is one side effect that will always occur, even with `installOrder: []`.
|
|
99
|
-
* The act of calling `agentify()` is enough to cause this side effect, by design.
|
|
100
|
-
*/
|
|
101
|
-
Module.runMain = async function (...args) {
|
|
102
|
-
try {
|
|
103
|
-
for (const err of config._errors) {
|
|
104
|
-
throw err; // move this into config itself since we now handle errors
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
logger.info('Starting %s v%s', core.agentName, core.agentVersion);
|
|
108
|
-
// pino serializers know how to log redacted values and omit certain props
|
|
109
|
-
logger.info({ config }, 'Agent configuration');
|
|
110
|
-
|
|
111
|
-
const plugin = await preRunMain(core);
|
|
112
|
-
|
|
113
|
-
for (const svcName of installOrder ?? []) {
|
|
114
|
-
const svc = core[svcName];
|
|
115
|
-
if (svc?.install) {
|
|
116
|
-
logger.trace('installing service: %s', svcName);
|
|
117
|
-
await svc.install();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (plugin?.install) {
|
|
122
|
-
await plugin.install();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
core.logDiagnosticFiles(); // should this be moved into a separate install side-effect?
|
|
126
|
-
} catch (err) {
|
|
127
|
-
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent
|
|
128
|
-
logger.error({ err }, ERROR_MESSAGE);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return runMain.apply(this, args);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
return core;
|
|
135
|
-
};
|
|
136
158
|
} catch (err) {
|
|
137
159
|
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent
|
|
138
160
|
core.agentify = function agentify() {
|
|
@@ -151,3 +173,5 @@ module.exports = function init(core = {}) {
|
|
|
151
173
|
|
|
152
174
|
return core.agentify;
|
|
153
175
|
};
|
|
176
|
+
|
|
177
|
+
module.exports.DEFAULT_INSTALL_ORDER = DEFAULT_INSTALL_ORDER;
|
package/lib/rewrite-hooks.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
'use strict';
|
|
18
18
|
|
|
19
|
-
const Module = require('module');
|
|
19
|
+
const Module = require('node:module');
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @param {import('.').Core & {
|
|
@@ -25,11 +25,12 @@ const Module = require('module');
|
|
|
25
25
|
* @returns {import('@contrast/common').Installable}
|
|
26
26
|
*/
|
|
27
27
|
module.exports = function init(core) {
|
|
28
|
+
const js = Module._extensions['.js'];
|
|
28
29
|
const { _compile } = Module.prototype;
|
|
29
30
|
|
|
30
31
|
core.rewriteHooks = {
|
|
31
32
|
install() {
|
|
32
|
-
if (!core.config.agent.node.
|
|
33
|
+
if (!core.config.agent.node.rewrite.enable) return;
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* @see https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js
|
|
@@ -37,35 +38,58 @@ module.exports = function init(core) {
|
|
|
37
38
|
* @param {string} filename The file path of the module
|
|
38
39
|
*/
|
|
39
40
|
Module.prototype._compile = function (content, filename) {
|
|
40
|
-
|
|
41
|
+
/** @type {import('@contrast/rewriter').RewriteOpts} */
|
|
41
42
|
const options = {
|
|
42
43
|
filename,
|
|
43
44
|
isModule: false,
|
|
44
45
|
inject: true,
|
|
45
46
|
wrap: true,
|
|
47
|
+
trim: false,
|
|
46
48
|
};
|
|
47
|
-
// if threadInfo is present, this is running with --loader or --import
|
|
48
|
-
core.threadInfo?.post('rewrite', options);
|
|
49
49
|
|
|
50
|
-
const
|
|
50
|
+
const result = core.rewriter.rewriteSync(content, options);
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
-
|
|
53
|
+
const compiled = Reflect.apply(_compile, this, [result.code, filename]);
|
|
54
|
+
|
|
55
|
+
if (core.config.agent.node.rewrite.cache.enable) {
|
|
56
|
+
core.rewriter.cache.write(filename, result);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return compiled;
|
|
54
60
|
} catch (err) {
|
|
55
61
|
core.logger.warn(
|
|
56
62
|
{ err },
|
|
57
63
|
'Failed to compile rewritten code for %s, compiling original code.',
|
|
58
64
|
filename,
|
|
59
65
|
);
|
|
60
|
-
|
|
66
|
+
|
|
67
|
+
return Reflect.apply(_compile, this, [content, filename]);
|
|
61
68
|
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @see https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js
|
|
73
|
+
* @param {Module} module The module to compile
|
|
74
|
+
* @param {string} filename The file path of the module
|
|
75
|
+
*/
|
|
76
|
+
Module._extensions['.js'] = function (module, filename) {
|
|
77
|
+
if (!core.config.agent.node.rewrite.cache.enable) {
|
|
78
|
+
return Reflect.apply(js, this, [module, filename]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const cached = core.rewriter.cache.readSync(filename);
|
|
62
82
|
|
|
63
|
-
|
|
83
|
+
// If cached, short circuit the _extensions method and go straight to compile.
|
|
84
|
+
return cached
|
|
85
|
+
? Reflect.apply(_compile, module, [cached, filename])
|
|
86
|
+
: Reflect.apply(js, this, [module, filename]);
|
|
64
87
|
};
|
|
65
88
|
},
|
|
66
89
|
|
|
67
90
|
uninstall() {
|
|
68
91
|
Module.prototype._compile = _compile;
|
|
92
|
+
Module._extensions['.js'] = js;
|
|
69
93
|
}
|
|
70
94
|
};
|
|
71
95
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agentify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
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)",
|
|
@@ -17,18 +17,18 @@
|
|
|
17
17
|
"test": "../scripts/test.sh"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@contrast/common": "1.
|
|
21
|
-
"@contrast/config": "1.
|
|
22
|
-
"@contrast/core": "1.
|
|
20
|
+
"@contrast/common": "1.19.0",
|
|
21
|
+
"@contrast/config": "1.26.0",
|
|
22
|
+
"@contrast/core": "1.30.0",
|
|
23
23
|
"@contrast/deadzones": "1.1.2",
|
|
24
24
|
"@contrast/dep-hooks": "1.3.1",
|
|
25
|
-
"@contrast/esm-hooks": "2.
|
|
26
|
-
"@contrast/instrumentation": "1.
|
|
27
|
-
"@contrast/logger": "1.
|
|
28
|
-
"@contrast/metrics": "1.
|
|
25
|
+
"@contrast/esm-hooks": "2.4.0",
|
|
26
|
+
"@contrast/instrumentation": "1.6.0",
|
|
27
|
+
"@contrast/logger": "1.8.0",
|
|
28
|
+
"@contrast/metrics": "1.6.0",
|
|
29
29
|
"@contrast/patcher": "1.7.1",
|
|
30
|
-
"@contrast/reporter": "1.
|
|
31
|
-
"@contrast/rewriter": "1.
|
|
30
|
+
"@contrast/reporter": "1.25.1",
|
|
31
|
+
"@contrast/rewriter": "1.5.0",
|
|
32
32
|
"@contrast/scopes": "1.4.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/lib/initialize.mjs
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright: 2024 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
|
-
import Module from 'node:module';
|
|
17
|
-
// want to only initialize some of the agent if main thread. not sure that's really
|
|
18
|
-
// possible. based on logging, it looks like all of the low-level hooks (rewrite-injection,
|
|
19
|
-
// assess-dataflow-propagators, assess-dataflow-sink ContrastMethods, and function-hooks)
|
|
20
|
-
// are added in both the main thread and the loader thread. But all other modules are loaded
|
|
21
|
-
// in the main thread. presumably, this is because the assess module was loaded in both
|
|
22
|
-
// threads, registered the appropriate patchers in each, so both convert 'import' (statement
|
|
23
|
-
// or function) to 'require'.
|
|
24
|
-
//
|
|
25
|
-
import { isMainThread, threadId } from 'node:worker_threads';
|
|
26
|
-
|
|
27
|
-
const ERROR_MESSAGE = 'An error prevented the Contrast agent from installing. The application will be run without instrumentation.';
|
|
28
|
-
const DEFAULT_INSTALL_ORDER = [
|
|
29
|
-
'reporter',
|
|
30
|
-
'contrastMethods',
|
|
31
|
-
'deadzones',
|
|
32
|
-
'scopes',
|
|
33
|
-
'sources',
|
|
34
|
-
'architectureComponents',
|
|
35
|
-
'assess',
|
|
36
|
-
'protect',
|
|
37
|
-
'depHooks',
|
|
38
|
-
'esmHooks',
|
|
39
|
-
'routeCoverage',
|
|
40
|
-
'libraryAnalysis',
|
|
41
|
-
'heapSnapshots',
|
|
42
|
-
'rewriteHooks',
|
|
43
|
-
'functionHooks',
|
|
44
|
-
'metrics',
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
const require = Module.createRequire(import.meta.url);
|
|
48
|
-
let startupError;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @param {object} { core = {}, options = {} }
|
|
52
|
-
*/
|
|
53
|
-
async function loadModules({ core = {}, options = {} }) {
|
|
54
|
-
try {
|
|
55
|
-
require('@contrast/core/lib/messages')(core);
|
|
56
|
-
require('@contrast/config')(core);
|
|
57
|
-
if (core.config._errors?.length) {
|
|
58
|
-
throw core.config._errors[0];
|
|
59
|
-
}
|
|
60
|
-
require('@contrast/logger').default(core);
|
|
61
|
-
|
|
62
|
-
const thread = isMainThread ? 'main' : 'loader';
|
|
63
|
-
core.logger.trace({ tid: threadId }, 'initializing core modules in %s thread', thread);
|
|
64
|
-
|
|
65
|
-
if (isMainThread) {
|
|
66
|
-
// @contrast/info ?
|
|
67
|
-
require('@contrast/core/lib/agent-info')(core);
|
|
68
|
-
require('@contrast/core/lib/system-info')(core);
|
|
69
|
-
require('@contrast/core/lib/app-info')(core);
|
|
70
|
-
if (core.appInfo._errors?.length) {
|
|
71
|
-
throw core.appInfo._errors[0];
|
|
72
|
-
}
|
|
73
|
-
require('@contrast/core/lib/sensitive-data-masking')(core);
|
|
74
|
-
require('@contrast/core/lib/is-agent-path')(core);
|
|
75
|
-
require('@contrast/dep-hooks')(core);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const { default: install } = await import('@contrast/esm-hooks');
|
|
79
|
-
const esmHooks = await install(core);
|
|
80
|
-
core.esmHooks = esmHooks;
|
|
81
|
-
|
|
82
|
-
if (isMainThread) {
|
|
83
|
-
require('@contrast/patcher')(core);
|
|
84
|
-
require('@contrast/core/lib/capture-stacktrace')(core);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
require('@contrast/rewriter')(core); // merge contrast-methods?
|
|
88
|
-
|
|
89
|
-
if (isMainThread) {
|
|
90
|
-
require('@contrast/core/lib/contrast-methods')(core); // can we remove dependency on patcher?
|
|
91
|
-
|
|
92
|
-
require('@contrast/scopes')(core);
|
|
93
|
-
require('@contrast/deadzones')(core);
|
|
94
|
-
require('@contrast/reporter').default(core);
|
|
95
|
-
require('@contrast/instrumentation')(core);
|
|
96
|
-
require('@contrast/metrics')(core);
|
|
97
|
-
|
|
98
|
-
require('./heap-snapshots')(core);
|
|
99
|
-
require('./sources')(core);
|
|
100
|
-
require('./function-hooks')(core);
|
|
101
|
-
require('./log-diagnostic-files')(core); // this doesn't really belong in agentify
|
|
102
|
-
require('./rewrite-hooks')(core);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
} catch (err) {
|
|
106
|
-
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent
|
|
107
|
-
if (core.logger) {
|
|
108
|
-
core.logger.error({ err, threadId }, ERROR_MESSAGE);
|
|
109
|
-
} else {
|
|
110
|
-
console.error(new Error(ERROR_MESSAGE, { cause: err }));
|
|
111
|
-
}
|
|
112
|
-
startupError = err;
|
|
113
|
-
}
|
|
114
|
-
return core;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function startAgent({ core, options = {} }) {
|
|
118
|
-
if (startupError) return;
|
|
119
|
-
|
|
120
|
-
const { executor, installOrder = DEFAULT_INSTALL_ORDER } = options;
|
|
121
|
-
const { config, logger } = core;
|
|
122
|
-
|
|
123
|
-
// this should be moved into config because errors are handled here now.
|
|
124
|
-
for (const error of config._errors) {
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
logger.info({ tid: threadId }, 'Starting %s v%s', core.agentName, core.agentVersion);
|
|
129
|
-
logger.info({ config }, 'Agent configuration');
|
|
130
|
-
|
|
131
|
-
let plugin;
|
|
132
|
-
if (typeof executor === 'function') {
|
|
133
|
-
plugin = await executor(core);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
for (const svcName of installOrder ?? []) {
|
|
137
|
-
const svc = core[svcName];
|
|
138
|
-
if (svc?.install) {
|
|
139
|
-
logger.trace({ tid: threadId }, 'installing service: %s', svcName);
|
|
140
|
-
await svc.install();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (plugin?.install) {
|
|
145
|
-
await plugin.install();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// should this be moved into a separate install side-effect?
|
|
149
|
-
if (isMainThread) {
|
|
150
|
-
core.logDiagnosticFiles();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return core;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export { loadModules, startAgent };
|