@contrast/agentify 1.42.1 → 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.
@@ -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 compile rewritten code for %s, compiling original code.',
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, complileSpy, rewriteHooks;
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
- complileSpy = sinon.spy(Module.prototype, '_compile');
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(complileSpy).to.have.been.calledWith(
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
- rewriteHooks.install();
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(complileSpy).to.have.been.calledWith(
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 compile rewritten code for %s, compiling original code.',
97
+ 'Failed to rewrite code for %s, continuing with original code.',
103
98
  testFilePath,
104
99
  );
105
- expect(complileSpy).to.have.been.calledWith(
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 compile rewritten code for %s, compiling original code.',
124
+ 'Failed to rewrite code for %s, continuing with original code.',
132
125
  testFilePath,
133
126
  );
134
- expect(complileSpy).to.have.been.calledWith(
127
+ expect(compileSpy).to.have.been.calledWith(
135
128
  testFileContent,
136
129
  testFilePath,
137
130
  );
package/lib/utils.js CHANGED
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agentify",
3
- "version": "1.42.1",
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)",
@@ -22,7 +22,7 @@
22
22
  "@contrast/core": "1.45.1",
23
23
  "@contrast/deadzones": "1.17.1",
24
24
  "@contrast/dep-hooks": "1.14.1",
25
- "@contrast/esm-hooks": "2.19.1",
25
+ "@contrast/esm-hooks": "2.19.2",
26
26
  "@contrast/find-package-json": "^1.1.0",
27
27
  "@contrast/instrumentation": "1.24.1",
28
28
  "@contrast/logger": "1.18.1",
@@ -30,7 +30,7 @@
30
30
  "@contrast/patcher": "1.17.1",
31
31
  "@contrast/perf": "1.3.1",
32
32
  "@contrast/reporter": "1.41.1",
33
- "@contrast/rewriter": "1.21.1",
33
+ "@contrast/rewriter": "1.21.2",
34
34
  "@contrast/scopes": "1.15.1",
35
35
  "on-finished": "^2.4.1",
36
36
  "semver": "^7.6.0"