@contrast/agentify 1.42.0 → 1.42.2
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/LICENSE +1 -1
- package/lib/diagnostics.js +1 -1
- package/lib/function-hooks.js +1 -1
- package/lib/heap-snapshots.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/rewrite-hooks.js +7 -5
- package/lib/rewrite-hooks.test.js +12 -19
- package/lib/sources.js +1 -1
- package/lib/utils.js +23 -1
- package/lib/utils.test.js +41 -20
- package/lib/validators.js +1 -1
- package/package.json +15 -15
package/LICENSE
CHANGED
package/lib/diagnostics.js
CHANGED
package/lib/function-hooks.js
CHANGED
package/lib/heap-snapshots.js
CHANGED
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
package/lib/rewrite-hooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2025 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -31,8 +31,6 @@ module.exports = function init(core) {
|
|
|
31
31
|
|
|
32
32
|
core.rewriteHooks = {
|
|
33
33
|
install() {
|
|
34
|
-
if (!core.config.agent.node.rewrite.enable) return;
|
|
35
|
-
|
|
36
34
|
// don't define this prior to install(), since it will interfere with other
|
|
37
35
|
// components that also need to instrument it e.g. dep-hooks.
|
|
38
36
|
_compile = Module.prototype._compile;
|
|
@@ -44,6 +42,10 @@ module.exports = function init(core) {
|
|
|
44
42
|
* @param {string} filename The file path of the module
|
|
45
43
|
*/
|
|
46
44
|
Module.prototype._compile = function (content, filename) {
|
|
45
|
+
if (!core.config.agent.node.rewrite.enable) {
|
|
46
|
+
return Reflect.apply(_compile, this, [content, filename]);
|
|
47
|
+
}
|
|
48
|
+
|
|
47
49
|
/** @type {import('@contrast/rewriter').RewriteOpts} */
|
|
48
50
|
const options = {
|
|
49
51
|
filename,
|
|
@@ -57,9 +59,9 @@ module.exports = function init(core) {
|
|
|
57
59
|
return Reflect.apply(_compile, this, [content, filename]);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
const result = core.rewriter.rewriteSync(content, options);
|
|
61
62
|
|
|
62
63
|
try {
|
|
64
|
+
const result = core.rewriter.rewriteSync(content, options);
|
|
63
65
|
const compiled = Reflect.apply(_compile, this, [result.code, filename]);
|
|
64
66
|
|
|
65
67
|
if (core.config.agent.node.rewrite.cache.enable) {
|
|
@@ -70,7 +72,7 @@ module.exports = function init(core) {
|
|
|
70
72
|
} catch (err) {
|
|
71
73
|
core.logger.warn(
|
|
72
74
|
{ err },
|
|
73
|
-
'Failed to
|
|
75
|
+
'Failed to rewrite code for %s, continuing with original code.',
|
|
74
76
|
filename,
|
|
75
77
|
);
|
|
76
78
|
|
|
@@ -12,7 +12,7 @@ const deadzonedFilePath = require.resolve('../test/node_modules/bcryptjs/dist/bc
|
|
|
12
12
|
const testFileContent = readFileSync(testFilePath).toString();
|
|
13
13
|
|
|
14
14
|
describe('agentify rewrite-hooks', function () {
|
|
15
|
-
let core,
|
|
15
|
+
let core, compileSpy, rewriteHooks;
|
|
16
16
|
|
|
17
17
|
beforeEach(function () {
|
|
18
18
|
core = mocks.core();
|
|
@@ -20,9 +20,10 @@ describe('agentify rewrite-hooks', function () {
|
|
|
20
20
|
core.config = mocks.config();
|
|
21
21
|
core.rewriter = mocks.rewriter();
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
compileSpy = sinon.spy(Module.prototype, '_compile');
|
|
24
24
|
|
|
25
25
|
rewriteHooks = require('./rewrite-hooks')(core);
|
|
26
|
+
rewriteHooks.install();
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
afterEach(function () {
|
|
@@ -32,7 +33,6 @@ describe('agentify rewrite-hooks', function () {
|
|
|
32
33
|
|
|
33
34
|
it('rewrites code when the cache returns no results expected', function () {
|
|
34
35
|
core.rewriter.cache.readSync.returns(undefined);
|
|
35
|
-
rewriteHooks.install();
|
|
36
36
|
|
|
37
37
|
require(testFilePath);
|
|
38
38
|
expect(core.rewriter.rewriteSync).to.have.been.calledWith(testFileContent, {
|
|
@@ -42,7 +42,7 @@ describe('agentify rewrite-hooks', function () {
|
|
|
42
42
|
wrap: true,
|
|
43
43
|
});
|
|
44
44
|
expect(core.logger.warn).not.to.have.been.called;
|
|
45
|
-
expect(
|
|
45
|
+
expect(compileSpy).to.have.been.calledWith(
|
|
46
46
|
core.rewriter.rewriteSync.getCall(0).returnValue.code,
|
|
47
47
|
testFilePath,
|
|
48
48
|
);
|
|
@@ -50,26 +50,23 @@ describe('agentify rewrite-hooks', function () {
|
|
|
50
50
|
|
|
51
51
|
it('does not not rewrite code when rewrite.enable is false', function () {
|
|
52
52
|
core.config.agent.node.rewrite.enable = false;
|
|
53
|
-
|
|
53
|
+
core.config.agent.node.rewrite.cache.enable = false;
|
|
54
54
|
|
|
55
55
|
require(testFilePath);
|
|
56
56
|
expect(core.rewriter.rewriteSync).not.to.have.been.called;
|
|
57
|
+
expect(compileSpy).to.have.been.calledWith(testFileContent, testFilePath);
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
it('does not not rewrite code when the cache returns results', function () {
|
|
60
|
-
rewriteHooks.install();
|
|
61
|
-
|
|
62
61
|
require(testFilePath);
|
|
63
62
|
expect(core.rewriter.rewriteSync).not.to.have.been.called;
|
|
64
|
-
expect(
|
|
63
|
+
expect(compileSpy).to.have.been.calledWith(
|
|
65
64
|
core.rewriter.cache.readSync.getCall(0).returnValue,
|
|
66
65
|
testFilePath,
|
|
67
66
|
);
|
|
68
67
|
});
|
|
69
68
|
|
|
70
|
-
it('logs a debug message when a file is rewrite-deadzoned', function() {
|
|
71
|
-
rewriteHooks.install();
|
|
72
|
-
|
|
69
|
+
it('logs a debug message when a file is rewrite-deadzoned', function () {
|
|
73
70
|
require(deadzonedFilePath);
|
|
74
71
|
|
|
75
72
|
expect(core.logger.debug).calledWith(
|
|
@@ -84,8 +81,6 @@ describe('agentify rewrite-hooks', function () {
|
|
|
84
81
|
code: content.replace('const variable', 'const variable const a'),
|
|
85
82
|
}));
|
|
86
83
|
|
|
87
|
-
rewriteHooks.install();
|
|
88
|
-
|
|
89
84
|
require(testFilePath);
|
|
90
85
|
expect(core.rewriter.rewriteSync).to.have.been.calledWith(testFileContent, {
|
|
91
86
|
filename: testFilePath,
|
|
@@ -99,10 +94,10 @@ describe('agentify rewrite-hooks', function () {
|
|
|
99
94
|
message: 'Missing initializer in const declaration',
|
|
100
95
|
}),
|
|
101
96
|
},
|
|
102
|
-
'Failed to
|
|
97
|
+
'Failed to rewrite code for %s, continuing with original code.',
|
|
103
98
|
testFilePath,
|
|
104
99
|
);
|
|
105
|
-
expect(
|
|
100
|
+
expect(compileSpy).to.have.been.calledWith(
|
|
106
101
|
testFileContent,
|
|
107
102
|
testFilePath,
|
|
108
103
|
);
|
|
@@ -113,8 +108,6 @@ describe('agentify rewrite-hooks', function () {
|
|
|
113
108
|
const testFilePath = require.resolve('../test/resources/file-bad');
|
|
114
109
|
const testFileContent = readFileSync(testFilePath).toString();
|
|
115
110
|
|
|
116
|
-
rewriteHooks.install();
|
|
117
|
-
|
|
118
111
|
expect(() => require(testFilePath)).to.throw('Unexpected identifier');
|
|
119
112
|
expect(core.rewriter.rewriteSync).to.have.been.calledWith(testFileContent, {
|
|
120
113
|
filename: testFilePath,
|
|
@@ -128,10 +121,10 @@ describe('agentify rewrite-hooks', function () {
|
|
|
128
121
|
message: sinon.match('Unexpected identifier'),
|
|
129
122
|
}),
|
|
130
123
|
},
|
|
131
|
-
'Failed to
|
|
124
|
+
'Failed to rewrite code for %s, continuing with original code.',
|
|
132
125
|
testFilePath,
|
|
133
126
|
);
|
|
134
|
-
expect(
|
|
127
|
+
expect(compileSpy).to.have.been.calledWith(
|
|
135
128
|
testFileContent,
|
|
136
129
|
testFilePath,
|
|
137
130
|
);
|
package/lib/sources.js
CHANGED
package/lib/utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2025 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
+
const { platform, arch } = require('os');
|
|
18
19
|
const path = require('path');
|
|
19
20
|
const process = require('process');
|
|
20
21
|
const semver = require('semver');
|
|
@@ -36,6 +37,7 @@ function preStartupValidation(core) {
|
|
|
36
37
|
assertNoExperimentalFeatureFlags();
|
|
37
38
|
assertSupportedNodeVersion(core.nodeEngines || nodeEngines);
|
|
38
39
|
assertSupportedPreloadUsage();
|
|
40
|
+
assertEnvironmentMatchesBuild();
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/**
|
|
@@ -57,6 +59,25 @@ function assertSupportedNodeVersion(engines) {
|
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Checks that some of our precompiled dependencies are included and callable.
|
|
64
|
+
* This should ensure that we're running in the same environment that we were
|
|
65
|
+
* installed in.
|
|
66
|
+
* @throws {Error}
|
|
67
|
+
*/
|
|
68
|
+
function assertEnvironmentMatchesBuild() {
|
|
69
|
+
try {
|
|
70
|
+
/* eslint-disable node/no-extraneous-require */
|
|
71
|
+
// since swc is a codependency of the agent it should always be present but
|
|
72
|
+
// doesn't necessarily need to be included in this module's dependencies.
|
|
73
|
+
require('@swc/core').parseSync('');
|
|
74
|
+
} catch (cause) {
|
|
75
|
+
throw (cause.message === 'Bindings not found.') ?
|
|
76
|
+
new Error(`Contrast cannot detect the correct precompiled dependencies for the current environment: ${platform()}-${arch()}. This typically occurs when deploying an installation from one environment to a different execution environment.`, { cause })
|
|
77
|
+
: cause;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
/**
|
|
61
82
|
* Checks that no experimental feature flags are used.
|
|
62
83
|
* @throws {Error}
|
|
@@ -169,5 +190,6 @@ module.exports = {
|
|
|
169
190
|
assertSupportedNodeVersion,
|
|
170
191
|
assertSupportedPreloadUsage,
|
|
171
192
|
assertNoExperimentalFeatureFlags,
|
|
193
|
+
assertEnvironmentMatchesBuild,
|
|
172
194
|
preStartupValidation,
|
|
173
195
|
};
|
package/lib/utils.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { expect } = require('chai');
|
|
4
|
+
const proxyquire = require('proxyquire');
|
|
4
5
|
const sinon = require('sinon');
|
|
5
6
|
|
|
6
7
|
const {
|
|
@@ -10,17 +11,17 @@ const {
|
|
|
10
11
|
assertNoExperimentalFeatureFlags,
|
|
11
12
|
} = require('./utils');
|
|
12
13
|
|
|
13
|
-
describe('preStartupValidation', function() {
|
|
14
|
+
describe('preStartupValidation', function () {
|
|
14
15
|
|
|
15
|
-
describe('assertValidOpts', function() {
|
|
16
|
-
it('Does not validate when noValidate is true', function() {
|
|
16
|
+
describe('assertValidOpts', function () {
|
|
17
|
+
it('Does not validate when noValidate is true', function () {
|
|
17
18
|
const opts = {
|
|
18
19
|
noValidate: true
|
|
19
20
|
};
|
|
20
21
|
expect(assertValidOpts(opts)).to.be.undefined;
|
|
21
22
|
});
|
|
22
23
|
|
|
23
|
-
it('Throws an error if install order is incorrect', function() {
|
|
24
|
+
it('Throws an error if install order is incorrect', function () {
|
|
24
25
|
const opts = {
|
|
25
26
|
installOrder: [
|
|
26
27
|
'telemetry',
|
|
@@ -32,40 +33,40 @@ describe('preStartupValidation', function() {
|
|
|
32
33
|
});
|
|
33
34
|
});
|
|
34
35
|
|
|
35
|
-
describe('assertSupportedNodeVersion', function() {
|
|
36
|
+
describe('assertSupportedNodeVersion', function () {
|
|
36
37
|
|
|
37
38
|
let engines;
|
|
38
|
-
beforeEach(function() {
|
|
39
|
+
beforeEach(function () {
|
|
39
40
|
engines = '>=16.9.1 <17 || >=18.7.0 <19 || >=20.6.0 <21 || >= 22.5.1 <23';
|
|
40
41
|
});
|
|
41
42
|
|
|
42
|
-
it('Does not throw an error if process.version is supported', function() {
|
|
43
|
+
it('Does not throw an error if process.version is supported', function () {
|
|
43
44
|
sinon.stub(process, 'version').value('v22.9.0');
|
|
44
45
|
expect(assertSupportedNodeVersion(engines)).to.be.undefined;
|
|
45
46
|
});
|
|
46
47
|
|
|
47
|
-
it('Throws an error if process.version is unsupported', function() {
|
|
48
|
+
it('Throws an error if process.version is unsupported', function () {
|
|
48
49
|
sinon.stub(process, 'version').value('v14.0.0');
|
|
49
50
|
expect(() => assertSupportedNodeVersion(engines)).to.throw('Contrast only officially supports Node LTS versions between 16.9.1 and 17, 18.7.0 and 19, 20.6.0 and 21, 22.5.1 and 23, but detected v14.0.0');
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
describe('assertSupportedPreloadUsage', function() {
|
|
55
|
+
describe('assertSupportedPreloadUsage', function () {
|
|
55
56
|
|
|
56
|
-
it('does not throw an err if --import flag is used (>= 18.19.0)', function() {
|
|
57
|
+
it('does not throw an err if --import flag is used (>= 18.19.0)', function () {
|
|
57
58
|
sinon.stub(process, 'execArgv').value(['--import', '@contrast/agent']);
|
|
58
59
|
sinon.stub(process, 'version').value('v22.9.0');
|
|
59
60
|
expect(assertSupportedPreloadUsage()).to.be.true;
|
|
60
61
|
});
|
|
61
62
|
|
|
62
|
-
it('throws an err if --import flag is used (<= 18.19.0)', function() {
|
|
63
|
+
it('throws an err if --import flag is used (<= 18.19.0)', function () {
|
|
63
64
|
sinon.stub(process, 'execArgv').value(['--import', '@contrast/agent']);
|
|
64
65
|
sinon.stub(process, 'version').value('v18.18.0');
|
|
65
66
|
expect(() => assertSupportedPreloadUsage()).to.throw('Contrast requires that Node LTS versions >= 18.19.0 use the --import flag for ESM support');
|
|
66
67
|
});
|
|
67
68
|
|
|
68
|
-
it('throws an err if --loader flag is used (>= 18.19.0)', function() {
|
|
69
|
+
it('throws an err if --loader flag is used (>= 18.19.0)', function () {
|
|
69
70
|
sinon.stub(process, 'execArgv').value(['--loader', '@contrast/agent']);
|
|
70
71
|
sinon.stub(process, 'version').value('v18.20.0');
|
|
71
72
|
expect(() => assertSupportedPreloadUsage()).to.throw('Contrast requires that Node versions less than 18.19.0 use the --loader flag for ESM support');
|
|
@@ -73,33 +74,33 @@ describe('preStartupValidation', function() {
|
|
|
73
74
|
|
|
74
75
|
});
|
|
75
76
|
|
|
76
|
-
describe('assertNoExperimentalFeatureFlags', function() {
|
|
77
|
-
describe('process.execArgv', function() {
|
|
78
|
-
it('Does not throw an error if no experimental feature flag is used', function() {
|
|
77
|
+
describe('assertNoExperimentalFeatureFlags', function () {
|
|
78
|
+
describe('process.execArgv', function () {
|
|
79
|
+
it('Does not throw an error if no experimental feature flag is used', function () {
|
|
79
80
|
sinon.stub(process, 'execArgv').value(['--import', '@contrast/agent']);
|
|
80
81
|
expect(assertNoExperimentalFeatureFlags()).to.be.undefined;
|
|
81
82
|
});
|
|
82
83
|
|
|
83
|
-
it('Throws an error if an experimental feature flag is used', function() {
|
|
84
|
+
it('Throws an error if an experimental feature flag is used', function () {
|
|
84
85
|
sinon.stub(process, 'execArgv').value(['--experimental-feature', '--import', '@contrast/agent']);
|
|
85
86
|
expect(() => assertNoExperimentalFeatureFlags()).to.throw('Contrast Agent does not support experimental features.');
|
|
86
87
|
});
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
describe('NODE_OPTIONS', function() {
|
|
90
|
+
describe('NODE_OPTIONS', function () {
|
|
90
91
|
|
|
91
92
|
const origNodeOptions = process.env?.NODE_OPTIONS;
|
|
92
93
|
|
|
93
|
-
afterEach(function() {
|
|
94
|
+
afterEach(function () {
|
|
94
95
|
process.env.NODE_OPTIONS = origNodeOptions;
|
|
95
96
|
});
|
|
96
97
|
|
|
97
|
-
it('Does not throw an error if no experimental feature flag is used', function() {
|
|
98
|
+
it('Does not throw an error if no experimental feature flag is used', function () {
|
|
98
99
|
process.env.NODE_OPTIONS = '--import @contrast/agent';
|
|
99
100
|
expect(assertNoExperimentalFeatureFlags()).to.be.undefined;
|
|
100
101
|
});
|
|
101
102
|
|
|
102
|
-
it('Throws an error if an experimental feature flag is used', function() {
|
|
103
|
+
it('Throws an error if an experimental feature flag is used', function () {
|
|
103
104
|
process.env.NODE_OPTIONS = '--experimental-feature --import @contrast/agent';
|
|
104
105
|
expect(() => assertNoExperimentalFeatureFlags()).to.throw('Contrast Agent does not support experimental features.');
|
|
105
106
|
});
|
|
@@ -107,4 +108,24 @@ describe('preStartupValidation', function() {
|
|
|
107
108
|
|
|
108
109
|
});
|
|
109
110
|
|
|
111
|
+
describe('assertEnvironmentMatchesBuild', function () {
|
|
112
|
+
let swcMock, utils;
|
|
113
|
+
|
|
114
|
+
beforeEach(function () {
|
|
115
|
+
swcMock = { parseSync: sinon.stub() };
|
|
116
|
+
|
|
117
|
+
utils = proxyquire('./utils', {
|
|
118
|
+
'@swc/core': swcMock,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('does not throw when dependencies are present and callable', function () {
|
|
123
|
+
expect(() => utils.assertEnvironmentMatchesBuild()).not.to.throw();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('throws when a dependency is missing or throws', function () {
|
|
127
|
+
swcMock.parseSync.throws(new Error('Bindings not found.'));
|
|
128
|
+
expect(() => utils.assertEnvironmentMatchesBuild()).to.throw();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
110
131
|
});
|
package/lib/validators.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agentify",
|
|
3
|
-
"version": "1.42.
|
|
3
|
+
"version": "1.42.2",
|
|
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,21 +17,21 @@
|
|
|
17
17
|
"test": "../scripts/test.sh"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@contrast/common": "1.29.
|
|
21
|
-
"@contrast/config": "1.40.
|
|
22
|
-
"@contrast/core": "1.45.
|
|
23
|
-
"@contrast/deadzones": "1.17.
|
|
24
|
-
"@contrast/dep-hooks": "1.14.
|
|
25
|
-
"@contrast/esm-hooks": "2.19.
|
|
20
|
+
"@contrast/common": "1.29.1",
|
|
21
|
+
"@contrast/config": "1.40.1",
|
|
22
|
+
"@contrast/core": "1.45.1",
|
|
23
|
+
"@contrast/deadzones": "1.17.1",
|
|
24
|
+
"@contrast/dep-hooks": "1.14.1",
|
|
25
|
+
"@contrast/esm-hooks": "2.19.2",
|
|
26
26
|
"@contrast/find-package-json": "^1.1.0",
|
|
27
|
-
"@contrast/instrumentation": "1.24.
|
|
28
|
-
"@contrast/logger": "1.18.
|
|
29
|
-
"@contrast/metrics": "1.22.
|
|
30
|
-
"@contrast/patcher": "1.17.
|
|
31
|
-
"@contrast/perf": "1.3.
|
|
32
|
-
"@contrast/reporter": "1.41.
|
|
33
|
-
"@contrast/rewriter": "1.21.
|
|
34
|
-
"@contrast/scopes": "1.15.
|
|
27
|
+
"@contrast/instrumentation": "1.24.1",
|
|
28
|
+
"@contrast/logger": "1.18.1",
|
|
29
|
+
"@contrast/metrics": "1.22.1",
|
|
30
|
+
"@contrast/patcher": "1.17.1",
|
|
31
|
+
"@contrast/perf": "1.3.1",
|
|
32
|
+
"@contrast/reporter": "1.41.1",
|
|
33
|
+
"@contrast/rewriter": "1.21.2",
|
|
34
|
+
"@contrast/scopes": "1.15.1",
|
|
35
35
|
"on-finished": "^2.4.1",
|
|
36
36
|
"semver": "^7.6.0"
|
|
37
37
|
}
|