@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 +1 -1
- package/bin/linux/contrast-service +0 -0
- package/bin/mac/contrast-service +0 -0
- package/bin/windows/contrast-service.exe +0 -0
- package/lib/agent.js +9 -2
- package/lib/app-info.js +88 -52
- package/lib/assess/loopback4/route-coverage.js +1 -1
- package/lib/contrast.js +25 -24
- package/lib/core/config/options.js +136 -238
- package/lib/hooks/frameworks/base.js +1 -1
- package/lib/hooks/module/helpers.js +1 -1
- package/lib/instrumentation.js +1 -1
- package/lib/libraries.js +119 -135
- package/lib/list-installed.js +13 -0
- package/lib/reporter/models/app-update/index.js +1 -1
- package/lib/reporter/translations/to-protobuf/dtm/agent-startup.js +3 -3
- package/lib/telemetry.js +188 -0
- package/lib/util/traverse.js +7 -3
- package/package.json +11 -9
package/bin/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.28.
|
|
1
|
+
2.28.17
|
|
Binary file
|
package/bin/mac/contrast-service
CHANGED
|
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
|
-
* @
|
|
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({
|
|
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
|
-
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
67
|
+
platform: os.platform(),
|
|
68
|
+
release: os.release(),
|
|
69
|
+
type: os.type()
|
|
44
70
|
};
|
|
45
71
|
this.hostname = os.hostname();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Actual application location (directory containing package.json)
|
|
103
|
+
* @type {string}
|
|
104
|
+
*/
|
|
105
|
+
this.appDir = path.dirname(this.path);
|
|
66
106
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.
|
|
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 (
|
|
86
|
-
|
|
113
|
+
if (isDir) {
|
|
114
|
+
this.indexFile = path.resolve(this.appDir, this.appPackage.main);
|
|
87
115
|
}
|
|
88
116
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
117
|
+
this.name = config.application.name || this.appPackage.name;
|
|
118
|
+
logger.info('using appname %s', this.name);
|
|
92
119
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
120
|
+
this.version = config.application.version || this.appPackage.version;
|
|
121
|
+
this.serverVersion = config.server.version || AGENT_INFO.VERSION;
|
|
96
122
|
|
|
97
|
-
|
|
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.
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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 <<');
|
|
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
|
-
|
|
362
|
-
console.log(
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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 =
|
|
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);
|