@contrast/core 1.43.0 → 1.45.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.
package/lib/app-info.js CHANGED
@@ -70,7 +70,11 @@ module.exports = function (core) {
70
70
 
71
71
 
72
72
  function checkPreLoadFlag() {
73
- const { version, execArgv } = process;
73
+ const {
74
+ version,
75
+ execArgv,
76
+ env: { NODE_OPTIONS },
77
+ } = process;
74
78
  [
75
79
  { range: '>=18.19.0', flags: ['--import'] },
76
80
  { range: '>=16.17.0 <18.19.0', flags: ['--loader'] },
@@ -78,7 +82,8 @@ module.exports = function (core) {
78
82
  ].forEach(({ range, flags }) => {
79
83
  if (
80
84
  semver.satisfies(version, range) &&
81
- !execArgv.some((el, idx) => el === '@contrast/agent' && flags.includes(execArgv[idx - 1]))
85
+ (execArgv.some((el, idx) => el === '@contrast/agent' && !flags.includes(execArgv[idx - 1])) ||
86
+ NODE_OPTIONS?.includes('@contrast/agent') && !flags.some(flag => NODE_OPTIONS.includes(flag)))
82
87
  ) {
83
88
  logger.warn(
84
89
  'For Node LTS %s, use %s command to run the agent. See: https://docs.contrastsecurity.com/en/install-node-js.html',
@@ -85,7 +85,24 @@ describe('core app-info', function () {
85
85
  { version: 'v18.18.0', range: '>=16.17.0 <18.19.0', badFlag: '-r', goodFlag: ['--loader'] },
86
86
  { version: 'v16.16.0', range: '<16.17.0', badFlag: '--loader', goodFlag: ['-r', '--require'] }
87
87
  ].forEach(({ version, range, badFlag, goodFlag }) => {
88
- it('logs a warning if non-recommended preload flag is used ', function() {
88
+
89
+ it(`does not log a warning if '@contrast/agent' is not part of exec args (${version})`, function() {
90
+ appInfo = proxyquire(
91
+ './app-info',
92
+ {
93
+ os,
94
+ process: {
95
+ ...process,
96
+ argv: ['node', __filename],
97
+ version,
98
+ execArgv: [badFlag, 'some/file']
99
+ }
100
+ }
101
+ )(core);
102
+ expect(core.logger.warn).to.not.have.been.called;
103
+ });
104
+
105
+ it(`logs a warning if non-recommended preload flag is used (${version})`, function() {
89
106
  appInfo = proxyquire(
90
107
  './app-info',
91
108
  {
@@ -104,6 +121,29 @@ describe('core app-info', function () {
104
121
  goodFlag
105
122
  );
106
123
  });
124
+
125
+ it(`logs a warning if non-recommended preload flag is used in NODE_OPTIONS (${version})`, function() {
126
+ appInfo = proxyquire(
127
+ './app-info',
128
+ {
129
+ os,
130
+ process: {
131
+ ...process,
132
+ argv: ['node', __filename],
133
+ version,
134
+ execArgv: [],
135
+ env: {
136
+ NODE_OPTIONS: `${badFlag} @contrast/agent`
137
+ }
138
+ }
139
+ }
140
+ )(core);
141
+ expect(core.logger.warn).to.have.been.calledWith(
142
+ 'For Node LTS %s, use %s command to run the agent. See: https://docs.contrastsecurity.com/en/install-node-js.html',
143
+ range,
144
+ goodFlag
145
+ );
146
+ });
107
147
  });
108
148
 
109
149
  [
@@ -111,7 +151,7 @@ describe('core app-info', function () {
111
151
  { version: 'v18.18.0', goodFlag: '--loader' },
112
152
  { version: 'v16.16.0', goodFlag: '--require' }
113
153
  ].forEach(({ version, goodFlag }) => {
114
- it('does not log a warning if recommended preload flag is used ', function() {
154
+ it('does not log a warning if recommended preload flag is used', function() {
115
155
  appInfo = proxyquire(
116
156
  './app-info',
117
157
  {
@@ -126,5 +166,24 @@ describe('core app-info', function () {
126
166
  )(core);
127
167
  expect(core.logger.warn).not.to.have.been.called;
128
168
  });
169
+
170
+ it('does not log a warning if recommended preload flag is used in NODE_OPTIONS', function() {
171
+ appInfo = proxyquire(
172
+ './app-info',
173
+ {
174
+ os,
175
+ process: {
176
+ ...process,
177
+ argv: ['node', __filename],
178
+ version,
179
+ execArgv: [],
180
+ env: {
181
+ NODE_OPTIONS: `${goodFlag} @contrast/agent`
182
+ }
183
+ }
184
+ }
185
+ )(core);
186
+ expect(core.logger.warn).not.to.have.been.called;
187
+ });
129
188
  });
130
189
  });
@@ -0,0 +1,51 @@
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
+
16
+ 'use strict';
17
+
18
+ const { CRC32 } = require('@tsxper/crc32');
19
+ const { readFile } = require('fs/promises');
20
+ const { resolve } = require('path');
21
+
22
+ /**
23
+ * @param {Object} core
24
+ * @param {import('@contrast/common').AppInfo} core.appInfo
25
+ * @returns
26
+ */
27
+ module.exports = function init(core) {
28
+ /** @type {string | undefined} */
29
+ let _buildId;
30
+
31
+ /**
32
+ * Attempts to hash the contents of the `package-lock.json` neighbor of the
33
+ * application's `package.json`. If no package-lock is detected, fall back to
34
+ * the app's `package.json` to generate the build ID.
35
+ * @returns {Promise<number | void>}
36
+ */
37
+ return core.getBuildId = async function getBuildId() {
38
+ if (_buildId) return _buildId;
39
+
40
+ const crc32 = new CRC32();
41
+ try {
42
+ const buf = await readFile(resolve(core.appInfo.app_dir, 'package-lock.json'));
43
+ return _buildId = crc32.forBuffer(buf).toString(16);
44
+ } catch (err) {
45
+ // unable to hash package-lock.json, fall back to stored package.json
46
+ }
47
+
48
+ const str = JSON.stringify(core.appInfo.pkg);
49
+ return _buildId = crc32.forString(str).toString(16);
50
+ };
51
+ };
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const proxyquire = require('proxyquire');
6
+
7
+ describe('core build-id', function () {
8
+ let fs, getBuildId;
9
+
10
+ beforeEach(function() {
11
+ fs = { readFile: sinon.stub().rejects() };
12
+
13
+ getBuildId = proxyquire('./build-id', { 'fs/promises': fs })({
14
+ appInfo: {
15
+ app_dir: '/app',
16
+ pkg: { 'name': 'test', 'main': 'index.js', 'dependencies': {} }
17
+ }
18
+ });
19
+ });
20
+
21
+ it('resolves a string when package-lock is found', async function () {
22
+ fs.readFile.onCall(0).resolves(
23
+ Buffer.from('{"name":"test","lockfileVersion":3,"requires":true,"packages":{}}')
24
+ );
25
+
26
+ expect(await getBuildId()).to.equal('c6378fdf');
27
+ });
28
+
29
+ it('resolves a string when package-lock is not found first', async function () {
30
+ expect(await getBuildId()).to.equal('55d7b2de');
31
+ });
32
+
33
+ it('memoizes the result', async function () {
34
+ expect(await getBuildId()).to.equal('55d7b2de');
35
+ expect(fs.readFile).to.have.callCount(1);
36
+ await getBuildId();
37
+ expect(fs.readFile).to.have.callCount(1);
38
+ });
39
+ });
package/lib/index.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
15
 
16
- import { AppInfo, Messages, SystemInfo } from '@contrast/common';
16
+ import { AppInfo, Messages, SystemInfo, ThreadTransferData } from '@contrast/common';
17
17
 
18
18
  interface Frame {
19
19
  eval: string | undefined;
@@ -30,7 +30,7 @@ interface CreateSnapshotOpts {
30
30
  }
31
31
 
32
32
  export interface Core {
33
- threadTransferData: any;
33
+ threadTransferData: ThreadTransferData;
34
34
  agentName: string;
35
35
  agentVersion: string;
36
36
  reportingInstance: string;
@@ -47,5 +47,6 @@ export interface Core {
47
47
  sensitiveDataMasking: any;
48
48
 
49
49
  getSystemInfo(): Promise<SystemInfo>;
50
+ getBuildId(): Promse<number | void>;
50
51
  Perf: any;
51
52
  }
package/lib/messages.js CHANGED
@@ -18,10 +18,11 @@
18
18
  const EventEmitter = require('events');
19
19
 
20
20
  module.exports = function init(core) {
21
- const messages = new EventEmitter();
21
+ const perf = new core.Perf('messages');
22
+ const messages = perf.wrapEmitter(new EventEmitter());
22
23
 
23
24
  // pad for number of components that listen for server-settings-update messages
24
- messages.setMaxListeners(20);
25
+ messages.setMaxListeners(10 + messages.getMaxListeners());
25
26
 
26
27
  return core.messages = messages;
27
28
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/core",
3
- "version": "1.43.0",
3
+ "version": "1.45.0",
4
4
  "description": "Preconfigured Contrast agent core services and models",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -16,12 +16,13 @@
16
16
  "test": "../scripts/test.sh"
17
17
  },
18
18
  "dependencies": {
19
- "@contrast/common": "1.28.0",
20
- "@contrast/config": "1.38.0",
19
+ "@contrast/common": "1.29.0",
20
+ "@contrast/config": "1.40.0",
21
21
  "@contrast/find-package-json": "^1.1.0",
22
22
  "@contrast/fn-inspect": "^4.3.0",
23
- "@contrast/logger": "1.16.0",
24
- "@contrast/patcher": "1.15.0",
23
+ "@contrast/logger": "1.18.0",
24
+ "@contrast/patcher": "1.17.0",
25
+ "@tsxper/crc32": "^2.1.3",
25
26
  "axios": "^1.7.4",
26
27
  "semver": "^7.6.0"
27
28
  }