@contrast/agentify 1.15.0 → 1.16.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 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
- interface AgentifyOptions {
39
- install: boolean;
40
- svcList: string[];
41
- }
42
-
43
- interface AgentOptions<T> {
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 PreRunMain<T> {
56
- (core: T, agent: Agent<T>): void | Promise<void>;
47
+ export interface AgentifyOptions {
48
+ installOrder?: string[];
57
49
  }
58
50
 
59
- export interface Agentify<T> {
60
- (preRunMain: PreRunMain<T>, opts?: AgentifyOptions): Agent<T>;
51
+ export interface PreRunMain {
52
+ (core: any): Installable | void | Promise<Installable | void>;
61
53
  }
62
54
 
63
- export interface Core<T> {
64
- readonly config: Config;
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<T>(core: Core<T>): Agentify<T>;
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 defaultOpts = {
23
- installOrder: [
24
- 'reporter',
25
- 'contrastMethods',
26
- 'deadzones',
27
- 'scopes',
28
- 'sources',
29
- 'architectureComponents',
30
- 'assess',
31
- 'protect',
32
- 'depHooks',
33
- 'routeCoverage',
34
- 'libraryAnalysis',
35
- 'heapSnapshots',
36
- 'rewriteHooks',
37
- 'functionHooks'
38
- ]
39
- };
40
-
41
- module.exports = function (core) {
42
- // compose add'l local services
43
- require('./heap-snapshots')(core);
44
- require('./sources')(core);
45
- require('./function-hooks')(core);
46
- require('./rewrite-hooks')(core);
47
-
48
-
49
- /**
50
- * The interface is a function, which when called, will hook runMain
51
- * @param {function} preRunMain
52
- * @param {object} opts
53
- */
54
- function agentify(preRunMain, opts = defaultOpts) {
55
- if (typeof preRunMain !== 'function') {
56
- throw new Error('Invalid usage: first argument must be a function');
57
- }
58
-
59
- opts.preRunMain = preRunMain;
60
-
61
- if (opts !== defaultOpts) {
62
- opts = { ...defaultOpts, ...opts };
63
- }
64
-
65
- return new Agent(core, opts);
66
- }
67
-
68
- core.agentify = agentify;
69
-
70
- return agentify;
71
- };
72
-
73
- class Agent {
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/events')(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
- * This is one side effect that will always occur, even with `install: false`.
91
- * The act of calling `agentify()` is enough to cause this side effect, by design.
76
+ * The interface is a function, which when called, will hook runMain
77
+ * @type {import('.').Agentify}
92
78
  */
93
- Module.runMain = async function (...args) {
94
- try {
95
- for (const err of config._errors) {
96
- throw err;
97
- }
98
-
99
- logger.info('Starting the Contrast agent');
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
- if (opts.installOrder?.length) {
105
- await self.install();
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
- if (plugin?.install) {
109
- await plugin.install();
110
- }
111
- } catch (err) {
112
- await self.handleInstallFailure(err);
113
- }
123
+ return runMain.apply(this, args);
124
+ };
114
125
 
115
- self.logDiagnosticFiles();
116
- return self.runMain.apply(this, args);
126
+ return core;
117
127
  };
118
- }
119
-
120
- /**
121
- * Original `runMain` will get called after this.
122
- *
123
- * TODO: Consider proper UNINSTALLATION and normal startup w/o agent
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
- // let these run async so they don't block agent startup.
152
- fs.writeFile(
153
- path.join(report_path, 'contrast_effective_config.json'),
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
- module.exports.Agent = Agent;
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agentify",
3
- "version": "1.15.0",
3
+ "version": "1.16.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)",
@@ -18,7 +18,16 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@contrast/common": "1.15.1",
21
- "@contrast/config": "1.20.0",
22
- "@contrast/logger": "1.6.0"
21
+ "@contrast/config": "1.21.0",
22
+ "@contrast/core": "1.26.1",
23
+ "@contrast/deadzones": "1.1.1",
24
+ "@contrast/dep-hooks": "^1.3.0",
25
+ "@contrast/instrumentation": "^1.2.1",
26
+ "@contrast/logger": "1.7.0",
27
+ "@contrast/metrics": "1.1.0",
28
+ "@contrast/patcher": "1.7.1",
29
+ "@contrast/reporter": "1.21.1",
30
+ "@contrast/rewriter": "^1.4.2",
31
+ "@contrast/scopes": "^1.4.0"
23
32
  }
24
33
  }