@contrast/agentify 1.40.0 → 1.42.0

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.
@@ -27,7 +27,7 @@ module.exports = function init(core) {
27
27
  logger
28
28
  } = core;
29
29
 
30
- const dir = path.join(process.cwd(), heap_dump.path);
30
+ const dir = path.resolve(process.cwd(), heap_dump.path);
31
31
  const ext = '.heapsnapshot';
32
32
 
33
33
  const heapSnapshots = {
@@ -30,7 +30,7 @@ describe('heap dump', function () {
30
30
  };
31
31
  delayMs = windowMs = 100;
32
32
  snapshotPath = 'contrast_heap_dumps';
33
- dirname = path.join(process.cwd(), snapshotPath);
33
+ dirname = path.resolve(process.cwd(), snapshotPath);
34
34
 
35
35
  core = {};
36
36
  core.config = mocks.config();
package/lib/index.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  import { Installable } from '@contrast/common';
17
17
  import { Config } from '@contrast/config';
18
- import { Perf } from '@contrast/perf';
18
+ import Perf from '@contrast/perf';
19
19
  import { Core as _Core } from '@contrast/core';
20
20
  import { Deadzones } from '@contrast/deadzones';
21
21
  import { DepHooks } from '@contrast/dep-hooks';
@@ -54,7 +54,7 @@ declare module 'node:module' {
54
54
 
55
55
  export interface Core extends _Core {
56
56
  config: Config;
57
- Perf: Perf;
57
+ Perf: typeof Perf;
58
58
  logger: Logger;
59
59
  depHooks: DepHooks;
60
60
  patcher: Patcher
package/lib/index.js CHANGED
@@ -83,7 +83,7 @@ module.exports = function init(core = {}) {
83
83
 
84
84
  let _callback;
85
85
  let _opts;
86
- const _perf = new core.Perf('agentify');
86
+ const _perfAgentify = new core.Perf('agentify');
87
87
 
88
88
  core.agentify = async function agentify(callback, opts = {}) {
89
89
  // options are hardcoded, so if this throws it's going to be in testing/dev situation
@@ -165,6 +165,7 @@ module.exports = function init(core = {}) {
165
165
  { name: 'agent-info', spec: '@contrast/core/lib/agent-info' },
166
166
  { name: 'app-info', spec: '@contrast/core/lib/app-info' },
167
167
  { name: 'system-info', spec: '@contrast/core/lib/system-info' },
168
+ { name: 'build-id', spec: '@contrast/core/lib/build-id' },
168
169
  // was check for appInfo errors length and throw if found
169
170
  { name: 'telemetry', spec: '@contrast/telemetry' },
170
171
  { name: 'sensitive-data-masking', spec: '@contrast/core/lib/sensitive-data-masking' },
@@ -217,7 +218,7 @@ module.exports = function init(core = {}) {
217
218
  mod = mod.default;
218
219
  }
219
220
 
220
- _perf.wrapInit(mod, spec)(core);
221
+ _perfAgentify.wrapInit(mod, spec)(core);
221
222
 
222
223
  // perform any validations that can take place now that this module is loaded.
223
224
  if (name === 'logger') {
@@ -17,7 +17,7 @@
17
17
  'use strict';
18
18
 
19
19
  const Module = require('node:module');
20
- const { rewriteIsDeadzoned } = require('./rewrite-is-deadzoned');
20
+ const { rewriteIsDeadzoned } = require('@contrast/rewriter/lib/rewrite-is-deadzoned');
21
21
 
22
22
  /**
23
23
  * @param {import('.').Core & {
@@ -26,13 +26,18 @@ const { rewriteIsDeadzoned } = require('./rewrite-is-deadzoned');
26
26
  * @returns {import('@contrast/common').Installable}
27
27
  */
28
28
  module.exports = function init(core) {
29
- const js = Module._extensions['.js'];
30
- const { _compile } = Module.prototype;
29
+ let js;
30
+ let _compile;
31
31
 
32
32
  core.rewriteHooks = {
33
33
  install() {
34
34
  if (!core.config.agent.node.rewrite.enable) return;
35
35
 
36
+ // don't define this prior to install(), since it will interfere with other
37
+ // components that also need to instrument it e.g. dep-hooks.
38
+ _compile = Module.prototype._compile;
39
+ js = Module._extensions['.js'];
40
+
36
41
  /**
37
42
  * @see https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js
38
43
  * @param {string} content The source code of the module
@@ -93,8 +98,8 @@ module.exports = function init(core) {
93
98
  },
94
99
 
95
100
  uninstall() {
96
- Module.prototype._compile = _compile;
97
- Module._extensions['.js'] = js;
101
+ _compile && (Module.prototype._compile = _compile);
102
+ js && (Module._extensions['.js'] = js);
98
103
  }
99
104
  };
100
105
 
package/lib/sources.js CHANGED
@@ -16,7 +16,8 @@
16
16
  'use strict';
17
17
 
18
18
  const { EventEmitter } = require('events');
19
- const { Event } = require('@contrast/common');
19
+ const onFinished = require('on-finished');
20
+ const { Event, primordials: { StringPrototypeToLowerCase } } = require('@contrast/common');
20
21
 
21
22
  module.exports = function(core) {
22
23
  const {
@@ -48,6 +49,7 @@ module.exports = function(core) {
48
49
  protocol: serverType === 'http' ? 'http' : 'https',
49
50
  serverType,
50
51
  time: Date.now(),
52
+ method: StringPrototypeToLowerCase.call(req.method),
51
53
  }
52
54
  };
53
55
 
@@ -61,7 +63,7 @@ module.exports = function(core) {
61
63
  });
62
64
  }
63
65
 
64
- res.on('finish', () => {
66
+ onFinished(res, (/* err, req */) => {
65
67
  messages.emit(Event.RESPONSE_FINISH, store);
66
68
  });
67
69
 
@@ -4,6 +4,7 @@ const EventEmitter = require('events');
4
4
  const { expect } = require('chai');
5
5
  const sinon = require('sinon');
6
6
  const { initProtectFixture } = require('@contrast/test/fixtures');
7
+ const proxyquire = require('proxyquire');
7
8
 
8
9
  describe('agentify sources', function () {
9
10
  [
@@ -51,7 +52,7 @@ describe('agentify sources', function () {
51
52
  }
52
53
  ].forEach(({ name, method, expected }) => {
53
54
  describe(`${name} sources using ${method || 'Server'}()`, function () {
54
- let core, api, ServerMock, reqMock, resMock;
55
+ let core, api, ServerMock, reqMock, resMock, onFinishedMock;
55
56
 
56
57
  beforeEach(function () {
57
58
  ({ core } = initProtectFixture());
@@ -83,9 +84,12 @@ describe('agentify sources', function () {
83
84
  url: 'http://localhost',
84
85
  };
85
86
  resMock = new EventEmitter();
87
+ onFinishedMock = sinon.stub();
86
88
 
87
89
  core.depHooks.resolve.withArgs(sinon.match({ name: 'http' })).yields(api);
88
- require('./sources')(core).install();
90
+ proxyquire('./sources', {
91
+ 'on-finished': onFinishedMock,
92
+ })(core).install();
89
93
  });
90
94
 
91
95
  it('"request" events run in scope with correct sourceInfo', function () {
@@ -103,7 +107,8 @@ describe('agentify sources', function () {
103
107
  protocol: 'http',
104
108
  serverType: 'http',
105
109
  });
106
- expect(resMock._events.finish).to.be.a('function');
110
+
111
+ expect(onFinishedMock).to.have.been.calledWith(resMock);
107
112
  });
108
113
 
109
114
  it('non-"request" events do not run in scope', function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agentify",
3
- "version": "1.40.0",
3
+ "version": "1.42.0",
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,22 @@
17
17
  "test": "../scripts/test.sh"
18
18
  },
19
19
  "dependencies": {
20
- "@contrast/common": "1.28.0",
21
- "@contrast/config": "1.38.0",
22
- "@contrast/core": "1.43.0",
23
- "@contrast/deadzones": "1.15.0",
24
- "@contrast/dep-hooks": "1.12.0",
25
- "@contrast/esm-hooks": "2.17.0",
20
+ "@contrast/common": "1.29.0",
21
+ "@contrast/config": "1.40.0",
22
+ "@contrast/core": "1.45.0",
23
+ "@contrast/deadzones": "1.17.0",
24
+ "@contrast/dep-hooks": "1.14.0",
25
+ "@contrast/esm-hooks": "2.19.0",
26
26
  "@contrast/find-package-json": "^1.1.0",
27
- "@contrast/instrumentation": "1.22.0",
28
- "@contrast/logger": "1.16.0",
29
- "@contrast/metrics": "1.20.0",
30
- "@contrast/patcher": "1.15.0",
31
- "@contrast/perf": "1.2.2",
32
- "@contrast/reporter": "1.39.0",
33
- "@contrast/rewriter": "1.19.0",
34
- "@contrast/scopes": "1.13.0",
27
+ "@contrast/instrumentation": "1.24.0",
28
+ "@contrast/logger": "1.18.0",
29
+ "@contrast/metrics": "1.22.0",
30
+ "@contrast/patcher": "1.17.0",
31
+ "@contrast/perf": "1.3.0",
32
+ "@contrast/reporter": "1.41.0",
33
+ "@contrast/rewriter": "1.21.0",
34
+ "@contrast/scopes": "1.15.0",
35
+ "on-finished": "^2.4.1",
35
36
  "semver": "^7.6.0"
36
37
  }
37
38
  }
@@ -1,143 +0,0 @@
1
- /*
2
- * Copyright: 2024 Contrast Security, Inc
3
- * Contact: support@contrastsecurity.com
4
- * License: Commercial
5
-
6
- * NOTICE: This Software and the patented inventions embodied within may only be
7
- * used as part of Contrast Security’s commercial offerings. Even though it is
8
- * made available through public repositories, use of this Software is subject to
9
- * the applicable End User Licensing Agreement found at
10
- * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
- * between Contrast Security and the End User. The Software may not be reverse
12
- * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
- * way not consistent with the End User License Agreement.
14
- */
15
- 'use strict';
16
-
17
- const { sep } = require('path');
18
-
19
- // todo: find optimal way to do these lookups
20
- const DEADZONED_PATHS = [
21
- 'ast-types', // CONTRAST-33909: `String` injection causes this module to crash.
22
- 'angular',
23
- 'acorn',
24
- 'archiver',
25
- 'archiver-utils',
26
- 'bcrypt',
27
- 'bcrypt-nodejs',
28
- 'bcryptjs', // node_modules/bcryptjs/index.js, node_modules/bcryptjs/dist/bcrypt.js
29
- '@babel', // this should handle all namespaced packages
30
- 'babel',
31
- 'babel-cli',
32
- 'babel-core',
33
- 'babel-traverse',
34
- 'babel-generator',
35
- 'babylon',
36
- 'bn.js',
37
- 'browserify',
38
- 'bson',
39
- 'bunyan',
40
- 'chai', // not sure why chai was rewritten
41
- 'coffeescript',
42
- 'compression',
43
- '@cyclonedx',
44
- 'etag',
45
- // 'cookie', // todo: verify this doesn't break sources/propagation (*)
46
- // 'cookie-signature', // (*)
47
- 'gzippo', // 149 weekly downloads
48
- // 'handlebars', // (*)
49
- 'handlebars-precompiler',
50
- // 'hbs', // ditto
51
- 'html-webpack-plugin',
52
- 'iconv-lite',
53
- 'jquery',
54
- 'jsrsasign',
55
- 'less',
56
- // 'dustjs-linkedin', // todo
57
- 'logger-console', // 2 weekly downloads
58
- 'loopback-datasource-juggler',
59
- 'moment',
60
- 'moment-timezone',
61
- 'node-forge',
62
- 'node-webpack',
63
- 'pem',
64
- 'react',
65
- 'react-dom', // doesn't this cover the next line?
66
- //'react-dom/server',
67
- 'requirejs',
68
- 'semver',
69
- 'strong-remoting',
70
- 'type-is',
71
- 'uglify-js',
72
- ];
73
-
74
- // maybe make the value an object for more complex strategies in the future
75
- // NOTE: they key should appear in the list above as well. if it's not there
76
- // then this object will never be checked.
77
- const CUSTOM_REWRITERS = {
78
- 'acorn': 'no-propagation',
79
- 'archiver': 'no-propagation',
80
- 'babel-core': 'no-propagation',
81
- '@babel': 'no-propagation',
82
- 'bcryptjs': 'no-propagation',
83
- 'bson': 'no-propagation',
84
- 'coffeescript': 'no-propagation',
85
- 'jsrsasign': 'no-propagation',
86
- 'less': 'no-propagation',
87
- '@cyclonedx': 'no-propagation',
88
- };
89
-
90
- const nodeModules = `${sep}node_modules${sep}`;
91
-
92
- function rewriteIsDeadzoned(absolutePath) {
93
- // we should only match the last node_modules folder
94
- const startingPoint = absolutePath.lastIndexOf(nodeModules) + nodeModules.length;
95
-
96
- for (const path of DEADZONED_PATHS) {
97
- const start = absolutePath.indexOf(path, startingPoint);
98
- // we return the name of the deadzoned module if it is found
99
- if (start >= 0 && (start + path.length === absolutePath.length || absolutePath[start + path.length] === sep)) {
100
- return path;
101
- }
102
- }
103
-
104
- return undefined;
105
- }
106
-
107
- // the next function is used if/when we implement custom rewrite strategies.
108
- // NODE-3512 implements that and this was taken from there.
109
-
110
- /**
111
- * Returns an array with a package name and that package's rewrite strategy.
112
- * The package name is only returned the package strategy is not 'default'.
113
- * Strategies:
114
- * - 'default': rewrite the module using the original, default rewriter
115
- * - 'deadzone': do not rewrite the module
116
- * - 'no-propagation': rewrite the module with the no-propagation rewriter
117
- *
118
- * why does this return the package name? mostly just because it had to extract
119
- * it from the path, so returning means the caller doesn't have to.
120
- *
121
- * @param {string} absolutePath
122
- * @returns {[string | undefined, 'default' | 'deadzone' | 'no-propagation']}
123
- */
124
- function getPackageRewriteStrategy(absolutePath) {
125
- const pkg = rewriteIsDeadzoned(absolutePath);
126
- if (!pkg) {
127
- return [undefined, 'default'];
128
- }
129
-
130
- const strategy = CUSTOM_REWRITERS[pkg];
131
- if (strategy && process.env.CSI_USE_CUSTOM_REWRITERS) {
132
- return [pkg, strategy];
133
- }
134
-
135
- return [pkg, 'deadzone'];
136
- }
137
-
138
- module.exports = {
139
- DEADZONED_PATHS,
140
- CUSTOM_REWRITERS,
141
- rewriteIsDeadzoned,
142
- getPackageRewriteStrategy,
143
- };
@@ -1,85 +0,0 @@
1
- 'use strict';
2
-
3
- const { normalize } = require('node:path');
4
- const { deepEqual } = require('node:assert').strict;
5
-
6
- const {
7
- getPackageRewriteStrategy, DEADZONED_PATHS, CUSTOM_REWRITERS
8
- } = require('./rewrite-is-deadzoned');
9
-
10
- describe('verify getPackageRewriteStrategy()', function() {
11
- before(function() {
12
- process.env.CSI_USE_CUSTOM_REWRITERS = '1';
13
- });
14
- after(function() {
15
- delete process.env.CSI_USE_CUSTOM_REWRITERS;
16
- });
17
-
18
- // test deadzoned paths and custom rewriters
19
- for (const path of DEADZONED_PATHS) {
20
- if (CUSTOM_REWRITERS[path]) {
21
- it(`should return "${CUSTOM_REWRITERS[path]}" for ${path}`, function() {
22
- const p = normalize(`/path/to/node_modules/${path}`);
23
- const result = getPackageRewriteStrategy(p);
24
- deepEqual(result, [path, CUSTOM_REWRITERS[path]]);
25
- });
26
- it(`should return "${CUSTOM_REWRITERS[path]}" for ${path} with additional elements`, function() {
27
- const p = normalize(`/path/to/node_modules/${path}/server`);
28
- const result = getPackageRewriteStrategy(p);
29
- deepEqual(result, [path, CUSTOM_REWRITERS[path]]);
30
- });
31
- it(`should return "${CUSTOM_REWRITERS[path]}" for ${path} with specific .js file`, function() {
32
- const p = normalize(`/path/to/node_modules/${path}/core/index.js`);
33
- const result = getPackageRewriteStrategy(p);
34
- deepEqual(result, [path, CUSTOM_REWRITERS[path]]);
35
- });
36
- } else {
37
- it(`should return "deadzone" for ${path}`, function() {
38
- const p = normalize(`/path/to/node_modules/${path}`);
39
- const result = getPackageRewriteStrategy(p);
40
- deepEqual(result, [path, 'deadzone']);
41
- });
42
- it(`should return "deadzone" for ${path} with additional elements`, function() {
43
- const p = normalize(`/path/to/node_modules/${path}/server`);
44
- const result = getPackageRewriteStrategy(p);
45
- deepEqual(result, [path, 'deadzone']);
46
- });
47
- it(`should return "deadzone" for ${path} with specific .js file`, function() {
48
- const p = normalize(`/path/to/node_modules/${path}/core/index.js`);
49
- const result = getPackageRewriteStrategy(p);
50
- deepEqual(result, [path, 'deadzone']);
51
- });
52
- }
53
- }
54
-
55
- // test default (not deadzoned or custom) paths
56
- for (const path of ['xyzzy', 'fubar', 'seven']) {
57
- it(`should return "default" for ${path}`, function() {
58
- const p = normalize(`/path/to/node_modules/${path}`);
59
- const result = getPackageRewriteStrategy(p);
60
- deepEqual(result, [undefined, 'default']);
61
- });
62
- it(`should return "default" for ${path} with additional elements`, function() {
63
- const p = normalize(`/path/to/node_modules/${path}/server`);
64
- const result = getPackageRewriteStrategy(p);
65
- deepEqual(result, [undefined, 'default']);
66
- });
67
- it(`should return "default" for ${path} with specific .js file`, function() {
68
- const p = normalize(`/path/to/node_modules/${path}/core/index.js`);
69
- const result = getPackageRewriteStrategy(p);
70
- deepEqual(result, [undefined, 'default']);
71
- });
72
- }
73
-
74
- const doubleCheck = [
75
- '/home/bruce/github/csi/node-mono/node_modules/@cyclonedx/cyclonedx-library/dist.node/serialize/xml/types.js'
76
- ];
77
-
78
- for (let path of doubleCheck) {
79
- path = normalize(path);
80
- it(`should return "no-propagation" for ${path}`, function() {
81
- const result = getPackageRewriteStrategy(path);
82
- deepEqual(result, ['@cyclonedx', 'no-propagation']);
83
- });
84
- }
85
- });