@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 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(deps) {
40
+ module.exports = function(core) {
37
41
  // compose add'l local services
38
- require('./sources')(deps);
39
- require('./contrast-methods')(deps);
40
- require('./function-hooks')(deps);
41
- require('./rewrite-hooks')(deps);
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(deps, opts);
63
+ return new Agent(core, opts);
60
64
  }
61
65
 
62
- deps.agentify = agentify;
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} deps dependencies
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(deps, opts) {
79
+ constructor(core, opts) {
76
80
  const self = this;
77
- const { config, logger } = deps;
81
+ const { config, logger } = core;
78
82
 
79
- this.deps = deps;
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(deps);
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.deps.logger.error(
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
- this.deps.logger.trace('installing service: %s', svcName);
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 { depHooks, patcher, logger, scopes: { sources: sourcesStorage } } = core;
20
- const sources = core.sources = {};
20
+ const {
21
+ instrumentation: { instrument },
22
+ scopes: { sources: sourcesStorage }
23
+ } = core;
21
24
 
22
- sources.install = function install() {
23
- depHooks.resolve({ name: 'http' }, http => patchServerEmit(http, 'httpServer'));
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
- return {
79
- install: sources.install,
80
- functions: {
81
- patchCreateServer,
82
- createServerPostHook,
83
- emitAroundHook
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.2.0",
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
+ }