@contrast/agentify 1.15.0 → 1.17.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/index.d.ts +15 -27
- package/lib/index.js +111 -137
- package/lib/log-diagnostic-files.js +46 -0
- package/lib/sources.js +2 -2
- package/package.json +13 -4
package/lib/index.d.ts
CHANGED
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { Config } from '@contrast/config';
|
|
17
|
+
import { Installable } from '@contrast/common';
|
|
17
18
|
import { Logger } from '@contrast/logger';
|
|
18
|
-
import RequireHook from '@contrast/require-hook';
|
|
19
19
|
import { Rewriter } from '@contrast/rewriter';
|
|
20
|
+
import RequireHook from '@contrast/require-hook';
|
|
20
21
|
|
|
21
22
|
declare module 'module' {
|
|
22
23
|
class Module {
|
|
@@ -35,39 +36,26 @@ declare module 'node:module' {
|
|
|
35
36
|
export = Module;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
install: boolean;
|
|
45
|
-
preRunMain: PreRunMain<T>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
declare class Agent<T> {
|
|
49
|
-
constructor(deps: T, opts: AgentOptions<T>);
|
|
50
|
-
hookRunMain(): void;
|
|
51
|
-
handleInstallFailure(err: Error): Promise<void>;
|
|
52
|
-
install(): Promise<void>;
|
|
39
|
+
// TODO: this is now much larger with all of the existing core deps
|
|
40
|
+
export interface Core {
|
|
41
|
+
readonly config: Config;
|
|
42
|
+
readonly depHooks: RequireHook;
|
|
43
|
+
readonly logger: Logger;
|
|
44
|
+
readonly rewriter: Rewriter;
|
|
53
45
|
}
|
|
54
46
|
|
|
55
|
-
interface
|
|
56
|
-
|
|
47
|
+
export interface AgentifyOptions {
|
|
48
|
+
installOrder?: string[];
|
|
57
49
|
}
|
|
58
50
|
|
|
59
|
-
export interface
|
|
60
|
-
(
|
|
51
|
+
export interface PreRunMain {
|
|
52
|
+
(core: any): Installable | void | Promise<Installable | void>;
|
|
61
53
|
}
|
|
62
54
|
|
|
63
|
-
export interface
|
|
64
|
-
|
|
65
|
-
readonly depHooks: RequireHook;
|
|
66
|
-
readonly logger: Logger;
|
|
67
|
-
readonly rewriter: Rewriter;
|
|
68
|
-
agentify: Agentify<T>;
|
|
55
|
+
export interface Agentify {
|
|
56
|
+
(preRunMain: PreRunMain, opts?: AgentifyOptions): any;
|
|
69
57
|
}
|
|
70
58
|
|
|
71
|
-
declare function init
|
|
59
|
+
declare function init(core: any): Agentify;
|
|
72
60
|
|
|
73
61
|
export = init;
|
package/lib/index.js
CHANGED
|
@@ -15,154 +15,128 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const fs = require('fs').promises;
|
|
19
|
-
const path = require('path');
|
|
20
18
|
const Module = require('module');
|
|
21
19
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
* @param {object} core dependencies
|
|
77
|
-
* @param {object} opts
|
|
78
|
-
* @param {function} opts.preRunMain custom function for registering services
|
|
79
|
-
* @param {boolean} opts.install whether to automatically install
|
|
80
|
-
*/
|
|
81
|
-
constructor(core, opts) {
|
|
82
|
-
const self = this;
|
|
83
|
-
const { config, logger } = core;
|
|
84
|
-
|
|
85
|
-
this.core = core;
|
|
86
|
-
this.opts = opts;
|
|
87
|
-
this.runMain = Module.runMain;
|
|
20
|
+
const ERROR_MESSAGE = 'A fatal agent installation error has occurred. The application will be run without instrumentation.';
|
|
21
|
+
const DEFAULT_INSTALL_ORDER = [
|
|
22
|
+
'reporter',
|
|
23
|
+
'contrastMethods',
|
|
24
|
+
'deadzones',
|
|
25
|
+
'scopes',
|
|
26
|
+
'sources',
|
|
27
|
+
'architectureComponents',
|
|
28
|
+
'assess',
|
|
29
|
+
'protect',
|
|
30
|
+
'depHooks',
|
|
31
|
+
'routeCoverage',
|
|
32
|
+
'libraryAnalysis',
|
|
33
|
+
'heapSnapshots',
|
|
34
|
+
'rewriteHooks',
|
|
35
|
+
'functionHooks',
|
|
36
|
+
'metrics',
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {any} core
|
|
41
|
+
* @returns {import('.').Agentify}
|
|
42
|
+
*/
|
|
43
|
+
module.exports = function init(core = {}) {
|
|
44
|
+
try {
|
|
45
|
+
require('@contrast/core/lib/messages')(core);
|
|
46
|
+
require('@contrast/config')(core);
|
|
47
|
+
require('@contrast/logger').default(core);
|
|
48
|
+
|
|
49
|
+
// @contrast/info ?
|
|
50
|
+
require('@contrast/core/lib/agent-info')(core);
|
|
51
|
+
require('@contrast/core/lib/system-info')(core);
|
|
52
|
+
require('@contrast/core/lib/app-info')(core);
|
|
53
|
+
require('@contrast/core/lib/sensitive-data-masking')(core);
|
|
54
|
+
require('@contrast/core/lib/is-agent-path')(core);
|
|
55
|
+
require('@contrast/core/lib/capture-stacktrace')(core);
|
|
56
|
+
|
|
57
|
+
require('@contrast/patcher')(core);
|
|
58
|
+
require('@contrast/rewriter')(core); // merge contrast-methods?
|
|
59
|
+
require('@contrast/core/lib/contrast-methods')(core); // can we remove dependency on patcher?
|
|
60
|
+
|
|
61
|
+
require('@contrast/dep-hooks')(core);
|
|
62
|
+
require('@contrast/scopes')(core);
|
|
63
|
+
require('@contrast/deadzones')(core);
|
|
64
|
+
require('@contrast/reporter').default(core);
|
|
65
|
+
require('@contrast/instrumentation')(core);
|
|
66
|
+
require('@contrast/metrics')(core);
|
|
67
|
+
|
|
68
|
+
// compose add'l local services
|
|
69
|
+
require('./heap-snapshots')(core);
|
|
70
|
+
require('./sources')(core);
|
|
71
|
+
require('./function-hooks')(core);
|
|
72
|
+
require('./log-diagnostic-files')(core); // this doesn't really belong in agentify
|
|
73
|
+
require('./rewrite-hooks')(core);
|
|
88
74
|
|
|
89
75
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
76
|
+
* The interface is a function, which when called, will hook runMain
|
|
77
|
+
* @type {import('.').Agentify}
|
|
92
78
|
*/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
logger.info({ config }, 'Agent configuration');
|
|
101
|
-
|
|
102
|
-
const plugin = await opts.preRunMain(core);
|
|
79
|
+
core.agentify = function agentify(
|
|
80
|
+
preRunMain,
|
|
81
|
+
{ installOrder = DEFAULT_INSTALL_ORDER } = {},
|
|
82
|
+
) {
|
|
83
|
+
if (typeof preRunMain !== 'function') {
|
|
84
|
+
throw new Error('Invalid usage: first argument must be a function');
|
|
85
|
+
}
|
|
103
86
|
|
|
104
|
-
|
|
105
|
-
|
|
87
|
+
const { runMain } = Module;
|
|
88
|
+
const { config, logger } = core;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* This is one side effect that will always occur, even with `installOrder: []`.
|
|
92
|
+
* The act of calling `agentify()` is enough to cause this side effect, by design.
|
|
93
|
+
*/
|
|
94
|
+
Module.runMain = async function (...args) {
|
|
95
|
+
try {
|
|
96
|
+
for (const err of config._errors) {
|
|
97
|
+
throw err; // move this into config itself since we now handle errors
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
logger.info('Starting %s v%s', core.agentName, core.agentVersion);
|
|
101
|
+
logger.info({ config }, 'Agent configuration');
|
|
102
|
+
|
|
103
|
+
const plugin = await preRunMain(core);
|
|
104
|
+
|
|
105
|
+
for (const svcName of installOrder ?? []) {
|
|
106
|
+
const svc = core[svcName];
|
|
107
|
+
if (svc?.install) {
|
|
108
|
+
logger.trace('installing service: %s', svcName);
|
|
109
|
+
await svc.install();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (plugin?.install) {
|
|
114
|
+
await plugin.install();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
core.logDiagnosticFiles(); // should this be moved into a separate install side-effect?
|
|
118
|
+
} catch (err) {
|
|
119
|
+
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent
|
|
120
|
+
logger.error({ err }, ERROR_MESSAGE);
|
|
106
121
|
}
|
|
107
122
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
} catch (err) {
|
|
112
|
-
await self.handleInstallFailure(err);
|
|
113
|
-
}
|
|
123
|
+
return runMain.apply(this, args);
|
|
124
|
+
};
|
|
114
125
|
|
|
115
|
-
|
|
116
|
-
return self.runMain.apply(this, args);
|
|
126
|
+
return core;
|
|
117
127
|
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
*
|
|
125
|
-
* @param {error} err Error thrown during install
|
|
126
|
-
*/
|
|
127
|
-
async handleInstallFailure(err) {
|
|
128
|
-
this.core.logger.error(
|
|
129
|
-
{ err },
|
|
130
|
-
'A fatal agent installation error has occurred. The application will be run without instrumentation.'
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async install() {
|
|
135
|
-
for (const svcName of this.opts.installOrder) {
|
|
136
|
-
const svc = this.core[svcName];
|
|
137
|
-
if (svc?.install) {
|
|
138
|
-
this.core.logger.trace('installing service: %s', svcName);
|
|
139
|
-
await svc.install();
|
|
140
|
-
}
|
|
128
|
+
} catch (err) {
|
|
129
|
+
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent
|
|
130
|
+
if (core.logger) {
|
|
131
|
+
core.logger.error({ err }, ERROR_MESSAGE);
|
|
132
|
+
} else {
|
|
133
|
+
console.error(new Error(ERROR_MESSAGE, { cause: err }));
|
|
141
134
|
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
logDiagnosticFiles() {
|
|
145
|
-
const { config, logger } = this.core;
|
|
146
|
-
|
|
147
|
-
if (!config.agent.diagnostics.enable) return;
|
|
148
|
-
|
|
149
|
-
const { report_path } = config.agent.diagnostics;
|
|
150
135
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
JSON.stringify(config.getReport({ redact: true }), null, 2)
|
|
155
|
-
).catch((err) => {
|
|
156
|
-
logger.warn({ err }, 'unable to write effective config file');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
fs.writeFile(
|
|
160
|
-
path.join(report_path, 'contrast_system_info.json'),
|
|
161
|
-
JSON.stringify(this.core.getSystemInfo(), null, 2)
|
|
162
|
-
).catch((err) => {
|
|
163
|
-
logger.warn({ err }, 'unable to write system info file');
|
|
164
|
-
});
|
|
136
|
+
core.agentify = function agentify() {
|
|
137
|
+
return core;
|
|
138
|
+
};
|
|
165
139
|
}
|
|
166
|
-
}
|
|
167
140
|
|
|
168
|
-
|
|
141
|
+
return core.agentify;
|
|
142
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2023 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
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const fs = require('fs/promises');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
module.exports = function init(core) {
|
|
22
|
+
core.logDiagnosticFiles = function logDiagnosticFiles() {
|
|
23
|
+
const { config, logger } = core;
|
|
24
|
+
|
|
25
|
+
if (!config.agent.diagnostics.enable) return;
|
|
26
|
+
|
|
27
|
+
const { report_path } = config.agent.diagnostics;
|
|
28
|
+
|
|
29
|
+
// let these run async so they don't block agent startup.
|
|
30
|
+
fs.writeFile(
|
|
31
|
+
path.join(report_path, 'contrast_effective_config.json'),
|
|
32
|
+
JSON.stringify(config.getReport({ redact: true }), null, 2)
|
|
33
|
+
).catch((err) => {
|
|
34
|
+
logger.warn({ err }, 'unable to write effective config file');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
fs.writeFile(
|
|
38
|
+
path.join(report_path, 'contrast_system_info.json'),
|
|
39
|
+
JSON.stringify(core.getSystemInfo(), null, 2)
|
|
40
|
+
).catch((err) => {
|
|
41
|
+
logger.warn({ err }, 'unable to write system info file');
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return core.logDiagnosticFiles;
|
|
46
|
+
};
|
package/lib/sources.js
CHANGED
|
@@ -20,7 +20,7 @@ const patchType = 'sources';
|
|
|
20
20
|
|
|
21
21
|
module.exports = function(core) {
|
|
22
22
|
const {
|
|
23
|
-
|
|
23
|
+
messages,
|
|
24
24
|
instrumentation: { instrument },
|
|
25
25
|
scopes: { sources: sourcesStorage }
|
|
26
26
|
} = core;
|
|
@@ -41,7 +41,7 @@ module.exports = function(core) {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
res.on('finish', () => {
|
|
44
|
-
|
|
44
|
+
messages.emit(Event.RESPONSE_FINISH, store);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
return sourcesStorage.run(store, next);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agentify",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.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,8 +17,17 @@
|
|
|
17
17
|
"test": "../scripts/test.sh"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@contrast/common": "1.
|
|
21
|
-
"@contrast/config": "1.
|
|
22
|
-
"@contrast/
|
|
20
|
+
"@contrast/common": "1.16.0",
|
|
21
|
+
"@contrast/config": "1.22.0",
|
|
22
|
+
"@contrast/core": "1.27.0",
|
|
23
|
+
"@contrast/deadzones": "1.1.1",
|
|
24
|
+
"@contrast/dep-hooks": "1.3.0",
|
|
25
|
+
"@contrast/instrumentation": "1.3.0",
|
|
26
|
+
"@contrast/logger": "1.7.0",
|
|
27
|
+
"@contrast/metrics": "1.2.0",
|
|
28
|
+
"@contrast/patcher": "1.7.1",
|
|
29
|
+
"@contrast/reporter": "1.22.0",
|
|
30
|
+
"@contrast/rewriter": "1.4.2",
|
|
31
|
+
"@contrast/scopes": "1.4.0"
|
|
23
32
|
}
|
|
24
33
|
}
|