@contrast/agent 4.10.6 → 4.12.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/bin/VERSION CHANGED
@@ -1 +1 @@
1
- 2.28.13
1
+ 2.28.17
Binary file
Binary file
Binary file
package/lib/agent.js CHANGED
@@ -31,8 +31,13 @@ class ContrastAgent {
31
31
  constructor() {
32
32
  /**
33
33
  * Instance of AppInfo containing application information.
34
- * @member */
34
+ * @type {import('./app-info')}
35
+ */
35
36
  this.appInfo = null;
37
+
38
+ /** @type {import('./telemetry') */
39
+ this.telemetry = null;
40
+
36
41
  /**
37
42
  * Holds user settings for the agent
38
43
  * @member */
@@ -94,7 +99,9 @@ class ContrastAgent {
94
99
  */
95
100
  this.argv = process.argv;
96
101
  this.tsFeatureSet._subscribers.push(this.agentEmitter);
97
- this.exclusions = new ExclusionFactory({ featureSet: this.tsFeatureSet });
102
+ this.exclusions = new ExclusionFactory({
103
+ featureSet: this.tsFeatureSet
104
+ });
98
105
  this.agentEmitter.on('application-settings', (applicationSettings) => {
99
106
  this.exclusions.updateSettings({
100
107
  settings: applicationSettings,
package/lib/app-info.js CHANGED
@@ -14,87 +14,123 @@ Copyright: 2022 Contrast Security, Inc
14
14
  */
15
15
  'use strict';
16
16
  const os = require('os');
17
+ const fs = require('fs');
17
18
  const path = require('path');
18
-
19
+ const parentPackageJson = require('parent-package-json');
20
+ const semver = require('semver');
21
+ const { AGENT_INFO } = require('./constants');
19
22
  const logger = require('./core/logger')('contrast:appInfo');
20
- const fileFinder = require('./util/file-finder');
21
- const {
22
- AGENT_INFO: { VERSION }
23
- } = require('./constants');
23
+
24
+ const isContainer = () => {
25
+ try {
26
+ fs.statSync('/.dockerenv');
27
+ return true;
28
+ } catch (err) {
29
+ // if no docker env, check /proc/self/cgroup
30
+ }
31
+
32
+ try {
33
+ return fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
34
+ } catch (err) {
35
+ // if file not present,
36
+ return false;
37
+ }
38
+ };
24
39
 
25
40
  /**
26
41
  * An AppInfo instance carries properties of the application being instrumented,
27
42
  * such as OS-related information, hostname, name and version information. The
28
43
  * name, version, and package.json data
29
44
  *
30
- * @class
31
- * @param {String} script File name/location for the app.
32
- * @param {} options Current configuration for the agent
33
- *
34
45
  * TODO(ehden): I'd like to get rid of this and put it more inline with what's happening in config/util.js,
35
46
  * but that's a bigger refactor and out of the scope of common config work.
36
47
  */
37
48
  class AppInfo {
49
+ /**
50
+ * @param {string} script File name/location for the app.
51
+ * @param {any} config Current configuration for the agent
52
+ */
38
53
  constructor(script, config) {
54
+ this.indexFile = path.resolve(script);
55
+ let isDir = false;
56
+
57
+ try {
58
+ const stats = fs.statSync(script);
59
+ isDir = stats.isDirectory();
60
+ } catch (err) {
61
+ // if we can't stat the start script we'll likely throw unless `app_root`
62
+ // is set. We'll let the logic below handle that.
63
+ }
64
+
39
65
  this.os = {
40
- type: os.type(),
41
- platform: os.platform(),
42
66
  architecture: os.arch(),
43
- release: os.release()
67
+ platform: os.platform(),
68
+ release: os.release(),
69
+ type: os.type()
44
70
  };
45
71
  this.hostname = os.hostname();
46
- const cmd = path.resolve(script);
47
- logger.info('finding package.json for %s', cmd);
48
- this.path = AppInfo.resolveAppPath(
49
- config.agent.node.app_root,
50
- path.dirname(cmd)
72
+ this.isContainer = isContainer();
73
+
74
+ logger.info('finding package.json for %s', this.indexFile);
75
+
76
+ // If started with `script` as a directory, append a file similar to below.
77
+ if (isDir) {
78
+ this.indexFile = path.join(this.indexFile, 'index.js');
79
+ }
80
+
81
+ const manifest = parentPackageJson(
82
+ // The `parent-package-json` library expects the start path to be a file,
83
+ // not a directory, so we append `package.json`. This lets us use the lib
84
+ // without changing the expected config option.
85
+ config.agent.node.app_root
86
+ ? path.join(config.agent.node.app_root, 'package.json')
87
+ : this.indexFile
51
88
  );
52
- config.agent.node.app_root = this.path;
53
89
 
54
- try {
55
- this.appPackage = require(this.path);
56
- } catch (e) {
90
+ if (!manifest) {
57
91
  throw new Error("Unable to find application's package.json.");
58
92
  }
59
- this.name = config.application.name || this.appPackage.name;
60
93
 
61
- const packageVersion = this.appPackage.version;
62
- this.version = config.application.version || packageVersion;
63
- this.serverVersion = config.server.version || VERSION;
94
+ /**
95
+ * Path to the application's package.json
96
+ * @type {string}
97
+ */
98
+ this.path = manifest.path;
99
+ logger.info('using package.json at %s', this.path);
64
100
 
65
- logger.info('using appname %s', this.name);
101
+ /**
102
+ * Actual application location (directory containing package.json)
103
+ * @type {string}
104
+ */
105
+ this.appDir = path.dirname(this.path);
66
106
 
67
- this.node_version = process.version;
68
- this.app_dir = path.dirname(this.path);
69
- this.appPath = config.application.path || this.app_dir;
70
- this.indexFile = script; // cli.js, app.js, index.js, server.js... etc
71
- this.serverName = config.server.name;
72
- this.serverEnvironment = config.server.environment;
73
- }
74
-
75
- /**
76
- * Returns the location of the app package
77
- *
78
- * @param {string} appRoot config.agent.node.app_root
79
- * @param {string} scriptPath directory the script is in
80
- * @returns {string} location of the app package
81
- */
82
- static resolveAppPath(appRoot, scriptPath) {
83
- let packageLocation;
107
+ /**
108
+ * Contents of the application's package.json
109
+ * @type {Record<string, any>}
110
+ */
111
+ this.appPackage = manifest.parse();
84
112
 
85
- if (appRoot) {
86
- packageLocation = fileFinder.findFile(appRoot, 'package.json');
113
+ if (isDir) {
114
+ this.indexFile = path.resolve(this.appDir, this.appPackage.main);
87
115
  }
88
116
 
89
- if (!packageLocation) {
90
- packageLocation = fileFinder.findFile(scriptPath, 'package.json');
91
- }
117
+ this.name = config.application.name || this.appPackage.name;
118
+ logger.info('using appname %s', this.name);
92
119
 
93
- if (packageLocation) {
94
- logger.info('using package.json at %s', packageLocation);
95
- }
120
+ this.version = config.application.version || this.appPackage.version;
121
+ this.serverVersion = config.server.version || AGENT_INFO.VERSION;
96
122
 
97
- return packageLocation;
123
+ this.nodeVersion = process.version;
124
+ this.nodeVersionMajor = semver.major(this.nodeVersion);
125
+
126
+ /**
127
+ * Configured application path to report
128
+ * @type {string}
129
+ */
130
+ this.appPath = config.application.path || this.appDir;
131
+
132
+ this.serverName = config.server.name;
133
+ this.serverEnvironment = config.server.environment;
98
134
  }
99
135
  }
100
136
 
@@ -24,7 +24,7 @@ const { funcinfo } = require('@contrast/fn-inspect');
24
24
  class RouteCoverage {
25
25
  constructor(agent) {
26
26
  this.routes = new Map();
27
- this.appDir = agent.appInfo.app_dir;
27
+ this.appDir = agent.appInfo.appDir;
28
28
  moduleHook.resolve(
29
29
  { name: '@loopback/rest', file: 'dist/router/routing-table.js' },
30
30
  this.patchRoutingTable.bind(this)
package/lib/contrast.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  /**
3
2
  Copyright: 2022 Contrast Security, Inc
4
3
  Contact: support@contrastsecurity.com
@@ -13,13 +12,8 @@ Copyright: 2022 Contrast Security, Inc
13
12
  engineered, modified, repackaged, sold, redistributed or otherwise used in a
14
13
  way not consistent with the End User License Agreement.
15
14
  */
16
- /**
17
- * Process flows to bootstrapping user code with the node agent code
18
- *
19
- * @module lib/contrastAgent
20
- *
21
- */
22
15
  'use strict';
16
+
23
17
  const { program } = require('./core/config/options');
24
18
  const path = require('path');
25
19
  const os = require('os');
@@ -27,8 +21,6 @@ const semver = require('semver');
27
21
  const colors = require('./util/colors');
28
22
  const { AGENT_INFO } = require('./constants');
29
23
  const { VERSION, SUPPORTED_VERSIONS, NAME } = AGENT_INFO;
30
- const contrastAgent = module.exports;
31
- const Promise = require('bluebird');
32
24
  const Module = require('module');
33
25
  const sourceMapUtility = require('./util/source-map');
34
26
  const loggerFactory = require('./core/logger');
@@ -43,10 +35,18 @@ function getAgentSnippet() {
43
35
  }
44
36
 
45
37
  let AppInfo,
38
+ /** @type {import('./agent')} */
46
39
  agent = {},
47
40
  TSReporter,
48
- tracker,
49
- instrument;
41
+ instrument,
42
+ /** @type {import('./telemetry')} */
43
+ Telemetry,
44
+ tracker;
45
+
46
+ /**
47
+ * Process flows to bootstrapping user code with the node agent code
48
+ */
49
+ const contrastAgent = module.exports;
50
50
 
51
51
  /**
52
52
  * Who doesn't like ASCII art. Displays Matt's cat when CONTRAST_CAT env var is set to MATT
@@ -56,7 +56,6 @@ contrastAgent.showBanner = function showBanner() {
56
56
  const fs = require('fs');
57
57
  const file = path.resolve(__dirname, 'cat.txt');
58
58
  const cat = fs.readFileSync(file, 'utf8');
59
- // eslint-disable-next-line no-console
60
59
  console.log(colors.red(cat));
61
60
  }
62
61
 
@@ -71,9 +70,10 @@ contrastAgent.showBanner = function showBanner() {
71
70
  contrastAgent.doImports = function doImports() {
72
71
  AppInfo = require('./app-info');
73
72
  agent = require('./agent');
74
- tracker = require('./tracker');
75
73
  TSReporter = require('./reporter/ts-reporter');
76
74
  instrument = require('./instrumentation');
75
+ Telemetry = require('./telemetry');
76
+ tracker = require('./tracker');
77
77
  };
78
78
 
79
79
  /**
@@ -229,7 +229,7 @@ contrastAgent.prepare = function(...args) {
229
229
 
230
230
  if (isCli) {
231
231
  logger.error(
232
- `DEPRECATED: Agent is started as runner. Please use '%s'`,
232
+ "DEPRECATED: Agent is started as runner. Please use '%s'",
233
233
  getAgentSnippet()
234
234
  );
235
235
  } else {
@@ -256,7 +256,7 @@ contrastAgent.prepare = function(...args) {
256
256
  }
257
257
 
258
258
  if (config.agent.node.dev.global_tracker) {
259
- logger.debug('>> config.agent.node.dev.global_tracker enabled <<'); // eslint-disable-line
259
+ logger.debug('>> config.agent.node.dev.global_tracker enabled <<');
260
260
  global.contrast_tracker = tracker;
261
261
  }
262
262
 
@@ -271,13 +271,12 @@ contrastAgent.prepare = function(...args) {
271
271
 
272
272
  config.version = semver.valid(semver.coerce(VERSION));
273
273
  agent.appInfo = app;
274
+ agent.telemetry = new Telemetry(agent, config);
274
275
 
275
276
  config.override('application.name', app.name);
276
277
 
277
278
  sourceMapUtility.init(agent);
278
279
 
279
- contrastAgent.logRewriteCacheInfo(agent);
280
-
281
280
  // return to startup if agent is enabled
282
281
  return config.enable;
283
282
  });
@@ -306,6 +305,7 @@ contrastAgent.bootstrap = function(args) {
306
305
  agent.clearIntervals();
307
306
  return;
308
307
  } else {
308
+ contrastAgent.logRewriteCacheInfo(agent);
309
309
  return instrument(agent, reporter);
310
310
  }
311
311
  })
@@ -358,12 +358,11 @@ contrastAgent.init = async function(args, isCli = false) {
358
358
  }
359
359
  })
360
360
  .on('--help', function() {
361
- // eslint-disable-next-line no-console
362
- console.log(`
363
- Example:
364
- ${colors.cyan(
365
- ` $ ${getAgentSnippet()} -c ../contrast_security.yaml -- --appArg1 --appArg2 -e -t -c`
366
- )}`);
361
+ console.log('Example:');
362
+ console.log(
363
+ colors.cyan('\t$ %s -c ../contrast_security.yaml -- --appArg1 --appArg2 -e -t -c'),
364
+ getAgentSnippet(),
365
+ );
367
366
  });
368
367
 
369
368
  await program.parseAsync(args);
@@ -407,7 +406,9 @@ contrastAgent.resetArgs = function(nodePath, script) {
407
406
  process.argv = agent.config
408
407
  ? [nodePath, script].concat(agent.config.application.args)
409
408
  : process.argv;
410
- const isPrimary = !agent.hasOwnProperty('cluster') || agent.cluster.isPrimary;
409
+ const isPrimary =
410
+ !Object.prototype.hasOwnProperty.call(agent, 'cluster') ||
411
+ agent.cluster.isPrimary;
411
412
  const location = isPrimary ? 'Entering main' : 'Entering fork';
412
413
 
413
414
  logger.debug('%s at %s', location, script);