@contrast/agentify 1.42.1 → 1.42.3
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/rewrite-hooks.js +6 -4
- package/lib/utils.js +22 -0
- package/package.json +17 -14
- package/lib/function-hooks.test.js +0 -174
- package/lib/heap-snapshots.test.js +0 -92
- package/lib/index.test.js +0 -188
- package/lib/rewrite-hooks.test.js +0 -139
- package/lib/sources.test.js +0 -127
- package/lib/utils.test.js +0 -110
- package/lib/validators.test.js +0 -78
package/lib/rewrite-hooks.js
CHANGED
|
@@ -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
|
|
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/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agentify",
|
|
3
|
-
"version": "1.42.
|
|
3
|
+
"version": "1.42.3",
|
|
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)",
|
|
7
7
|
"files": [
|
|
8
|
-
"lib/"
|
|
8
|
+
"lib/",
|
|
9
|
+
"!*.test.*",
|
|
10
|
+
"!tsconfig.*",
|
|
11
|
+
"!*.map"
|
|
9
12
|
],
|
|
10
13
|
"main": "lib/index.js",
|
|
11
14
|
"types": "lib/index.d.ts",
|
|
@@ -18,20 +21,20 @@
|
|
|
18
21
|
},
|
|
19
22
|
"dependencies": {
|
|
20
23
|
"@contrast/common": "1.29.1",
|
|
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.
|
|
24
|
+
"@contrast/config": "1.40.2",
|
|
25
|
+
"@contrast/core": "1.45.2",
|
|
26
|
+
"@contrast/deadzones": "1.17.2",
|
|
27
|
+
"@contrast/dep-hooks": "1.14.2",
|
|
28
|
+
"@contrast/esm-hooks": "2.19.3",
|
|
26
29
|
"@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.
|
|
30
|
+
"@contrast/instrumentation": "1.24.2",
|
|
31
|
+
"@contrast/logger": "1.18.2",
|
|
32
|
+
"@contrast/metrics": "1.22.2",
|
|
33
|
+
"@contrast/patcher": "1.17.2",
|
|
31
34
|
"@contrast/perf": "1.3.1",
|
|
32
|
-
"@contrast/reporter": "1.41.
|
|
33
|
-
"@contrast/rewriter": "1.21.
|
|
34
|
-
"@contrast/scopes": "1.15.
|
|
35
|
+
"@contrast/reporter": "1.41.2",
|
|
36
|
+
"@contrast/rewriter": "1.21.3",
|
|
37
|
+
"@contrast/scopes": "1.15.2",
|
|
35
38
|
"on-finished": "^2.4.1",
|
|
36
39
|
"semver": "^7.6.0"
|
|
37
40
|
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const mocks = require('@contrast/test/mocks');
|
|
5
|
-
const functionHooks = require('./function-hooks');
|
|
6
|
-
|
|
7
|
-
describe('agentify function-hooks', function () {
|
|
8
|
-
let core;
|
|
9
|
-
|
|
10
|
-
beforeEach(function () {
|
|
11
|
-
core = mocks.core();
|
|
12
|
-
core.patcher = mocks.patcher();
|
|
13
|
-
core.logger = mocks.logger();
|
|
14
|
-
core.depHooks = mocks.depHooks();
|
|
15
|
-
core.scopes = mocks.scopes();
|
|
16
|
-
core.rewriter = mocks.rewriter();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should install function hooks', function () {
|
|
20
|
-
functionHooks(core);
|
|
21
|
-
core.functionHooks.install();
|
|
22
|
-
expect(core.functionHooks.installed).to.be.true;
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should skip if it is already installed', function () {
|
|
26
|
-
functionHooks(core);
|
|
27
|
-
core.functionHooks.installed = true;
|
|
28
|
-
core.functionHooks.install();
|
|
29
|
-
|
|
30
|
-
expect(core.logger.debug).not.to.have.been.called;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should return cached value when the function is already called', function () {
|
|
34
|
-
const functionA = function sum(a, b) {
|
|
35
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
36
|
-
return a + b;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const cachedValue = 'cachedValue';
|
|
40
|
-
|
|
41
|
-
functionHooks(core);
|
|
42
|
-
|
|
43
|
-
core.functionHooks.cache.set(functionA, cachedValue);
|
|
44
|
-
|
|
45
|
-
const data = {
|
|
46
|
-
obj: functionA,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
let result;
|
|
50
|
-
core.patcher.patch.callsFake((prototype, method, options) => {
|
|
51
|
-
result = options.around(() => ({}), data);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
core.functionHooks.install();
|
|
55
|
-
|
|
56
|
-
expect(result).to.equal(cachedValue);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should call the function itself when the function is not hooked', function () {
|
|
60
|
-
const functionA = function sum(a, b) {
|
|
61
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
62
|
-
return a + b;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
functionHooks(core);
|
|
66
|
-
|
|
67
|
-
const data = {
|
|
68
|
-
obj: functionA,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
let result;
|
|
72
|
-
core.patcher.patch.callsFake((prototype, method, options) => {
|
|
73
|
-
result = options.around(functionA.toString.bind(functionA), data);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const unwriteResult = `function sum(a, b) {
|
|
77
|
-
eval(2 + 2);
|
|
78
|
-
return a + b;
|
|
79
|
-
}`;
|
|
80
|
-
|
|
81
|
-
core.rewriter.unwriteSync.callsFake((data) => unwriteResult);
|
|
82
|
-
|
|
83
|
-
core.functionHooks.install();
|
|
84
|
-
|
|
85
|
-
expect(result).to.equal(unwriteResult);
|
|
86
|
-
expect(core.functionHooks.cache.get(data.obj)).to.equal(unwriteResult);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should the call the original function of the hooked function', function () {
|
|
90
|
-
const functionA = function sum(a, b) {
|
|
91
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
92
|
-
return a + b;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const functionB = function sum(a, b) {
|
|
96
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
97
|
-
return a + b + 2;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
functionHooks(core);
|
|
101
|
-
|
|
102
|
-
const weekMap = core.patcher.hookedFunctions;
|
|
103
|
-
weekMap.set(functionA, { fn: functionB });
|
|
104
|
-
|
|
105
|
-
const data = {
|
|
106
|
-
obj: functionA,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
let result;
|
|
110
|
-
core.patcher.patch.callsFake((prototype, method, options) => {
|
|
111
|
-
result = options.around(functionA.toString.bind(functionA), data);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const unwriteResult = `function sum(a, b) {
|
|
115
|
-
eval(2 + 2);
|
|
116
|
-
return a + b + 2;
|
|
117
|
-
}`;
|
|
118
|
-
|
|
119
|
-
core.rewriter.unwriteSync.callsFake((data) => unwriteResult);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
core.functionHooks.install();
|
|
123
|
-
|
|
124
|
-
expect(result).to.equal(unwriteResult);
|
|
125
|
-
expect(core.functionHooks.cache.get(data.obj)).to.equal(unwriteResult);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should log warning message when there is a problem unwriting the code', function () {
|
|
129
|
-
const err = new Error('Error');
|
|
130
|
-
const functionA = function sum(a, b) {
|
|
131
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
132
|
-
return a + b;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
functionHooks(core);
|
|
136
|
-
|
|
137
|
-
const data = {
|
|
138
|
-
obj: functionA,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
core.patcher.patch.callsFake((prototype, method, options) => {
|
|
142
|
-
options.around(functionA.toString.bind(functionA), data);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
core.rewriter.unwriteSync.throws(err);
|
|
146
|
-
|
|
147
|
-
core.functionHooks.install();
|
|
148
|
-
|
|
149
|
-
expect(core.logger.warn).to.have.been.calledWith({ err, code: functionA.toString() }, 'Failed to unwrite function code');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should call the original toString if the function is not hooked', function () {
|
|
153
|
-
const functionA = () => 1;
|
|
154
|
-
functionHooks(core);
|
|
155
|
-
core.functionHooks.install();
|
|
156
|
-
|
|
157
|
-
const resultOfA = functionA.toString();
|
|
158
|
-
|
|
159
|
-
expect(resultOfA).to.equal('() => 1');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should uninstall the hooks', function () {
|
|
163
|
-
const { toString } = Function.prototype;
|
|
164
|
-
const functionA = () => 1;
|
|
165
|
-
functionHooks(core);
|
|
166
|
-
core.functionHooks.install();
|
|
167
|
-
core.functionHooks.uninstall();
|
|
168
|
-
|
|
169
|
-
const resultOfA = functionA.toString();
|
|
170
|
-
|
|
171
|
-
expect(resultOfA).to.equal('() => 1');
|
|
172
|
-
expect(Function.prototype.toString).to.equal(toString);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const proxyquire = require('proxyquire');
|
|
6
|
-
const sinon = require('sinon');
|
|
7
|
-
const { ConfigSource: { ENVIRONMENT_VARIABLE } } = require('@contrast/config');
|
|
8
|
-
const mocks = require('@contrast/test/mocks');
|
|
9
|
-
const { delay } = require('@contrast/test/utils');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
describe('heap dump', function () {
|
|
13
|
-
let core,
|
|
14
|
-
heapSnapshots,
|
|
15
|
-
fsMock,
|
|
16
|
-
v8Mock,
|
|
17
|
-
dirname,
|
|
18
|
-
delayMs,
|
|
19
|
-
windowMs,
|
|
20
|
-
snapshotPath;
|
|
21
|
-
|
|
22
|
-
beforeEach(function () {
|
|
23
|
-
fsMock = {
|
|
24
|
-
promises: {
|
|
25
|
-
mkdir: sinon.stub().resolves(''),
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
v8Mock = {
|
|
29
|
-
writeHeapSnapshot: sinon.stub(),
|
|
30
|
-
};
|
|
31
|
-
delayMs = windowMs = 100;
|
|
32
|
-
snapshotPath = 'contrast_heap_dumps';
|
|
33
|
-
dirname = path.resolve(process.cwd(), snapshotPath);
|
|
34
|
-
|
|
35
|
-
core = {};
|
|
36
|
-
core.config = mocks.config();
|
|
37
|
-
core.logger = mocks.logger();
|
|
38
|
-
|
|
39
|
-
core.config.setValue('agent.heap_dump.enable', true, ENVIRONMENT_VARIABLE);
|
|
40
|
-
core.config.setValue('agent.heap_dump.path', snapshotPath, ENVIRONMENT_VARIABLE);
|
|
41
|
-
core.config.setValue('agent.heap_dump.delay_ms', delayMs, ENVIRONMENT_VARIABLE);
|
|
42
|
-
core.config.setValue('agent.heap_dump.window_ms', windowMs, ENVIRONMENT_VARIABLE);
|
|
43
|
-
core.config.setValue('agent.heap_dump.count', 2, ENVIRONMENT_VARIABLE);
|
|
44
|
-
|
|
45
|
-
const init = proxyquire('./heap-snapshots', {
|
|
46
|
-
fs: fsMock,
|
|
47
|
-
v8: v8Mock,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
heapSnapshots = init(core);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('.writeHeapSnapshot()', function () {
|
|
54
|
-
it('writes snapshots', function () {
|
|
55
|
-
heapSnapshots.writeHeapSnapshot();
|
|
56
|
-
expect(core.logger.info).to.have.been.calledWithMatch(
|
|
57
|
-
'Writing heap snapshot at %s',
|
|
58
|
-
dirname,
|
|
59
|
-
);
|
|
60
|
-
expect(v8Mock.writeHeapSnapshot).to.have.been.calledWithMatch(dirname);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
describe('.install()', function () {
|
|
65
|
-
it('logs when the directory cannot be created', async function () {
|
|
66
|
-
const err = new Error('ENOENT');
|
|
67
|
-
fsMock.promises.mkdir.rejects(err);
|
|
68
|
-
await heapSnapshots.install();
|
|
69
|
-
expect(core.logger.error).to.have.been.calledOnceWithExactly({
|
|
70
|
-
err,
|
|
71
|
-
}, 'Unable to create snapshot directory. No heap snapshots will be taken.');
|
|
72
|
-
expect(v8Mock.writeHeapSnapshot).not.to.have.been.called;
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
it('does not write snapshots if enable is false', function () {
|
|
77
|
-
core.config.setValue('agent.heap_dumps.enable', false, ENVIRONMENT_VARIABLE);
|
|
78
|
-
heapSnapshots.install();
|
|
79
|
-
expect(v8Mock.writeHeapSnapshot).not.to.have.been.called;
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('writes snapshots at the correct intervals', async function () {
|
|
83
|
-
heapSnapshots.install();
|
|
84
|
-
expect(v8Mock.writeHeapSnapshot).not.to.have.been.called;
|
|
85
|
-
const pad = 50;
|
|
86
|
-
await delay(delayMs + windowMs + pad);
|
|
87
|
-
expect(v8Mock.writeHeapSnapshot).to.have.callCount(1);
|
|
88
|
-
await delay(windowMs);
|
|
89
|
-
expect(v8Mock.writeHeapSnapshot).to.have.callCount(2);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
});
|
package/lib/index.test.js
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const proxyquire = require('proxyquire');
|
|
5
|
-
const sinon = require('sinon');
|
|
6
|
-
const mocks = require('@contrast/test/mocks');
|
|
7
|
-
const Perf = require('@contrast/perf');
|
|
8
|
-
|
|
9
|
-
describe('agentify', function () {
|
|
10
|
-
const Module = require('module');
|
|
11
|
-
let agentify, preRunMain, runMain;
|
|
12
|
-
const allAgentify = new Map();
|
|
13
|
-
// the sinon stubs are captured in beforeEach(), before the agentify call,
|
|
14
|
-
// because the Perf wrapper will replace them on the core object. this way we
|
|
15
|
-
// can still determine that the function was called/not called as expected.
|
|
16
|
-
let loggerError;
|
|
17
|
-
let depHooksInstall;
|
|
18
|
-
|
|
19
|
-
beforeEach(function () {
|
|
20
|
-
// added because the config validation code verifies that the agent has
|
|
21
|
-
// the three required settings and throws if not.
|
|
22
|
-
process.env.CONTRAST__API__API_KEY = 'testing';
|
|
23
|
-
process.env.CONTRAST__API__SERVICE_KEY = 'testing';
|
|
24
|
-
process.env.CONTRAST__API__USER_NAME = 'testing';
|
|
25
|
-
|
|
26
|
-
agentify = proxyquire('.', {
|
|
27
|
-
'@contrast/logger': {
|
|
28
|
-
default(core) {
|
|
29
|
-
core.logger = mocks.logger();
|
|
30
|
-
loggerError = core.logger.error;
|
|
31
|
-
return core.logger;
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
'@contrast/dep-hooks'(core) {
|
|
35
|
-
core.depHooks = mocks.depHooks();
|
|
36
|
-
depHooksInstall = core.depHooks.install;
|
|
37
|
-
return core.depHooks;
|
|
38
|
-
},
|
|
39
|
-
})();
|
|
40
|
-
preRunMain = sinon.stub();
|
|
41
|
-
runMain = sinon.stub(Module, 'runMain');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
afterEach(function() {
|
|
45
|
-
Perf.fromAllToMap('agentify', allAgentify);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
after(function() {
|
|
49
|
-
delete process.env.CONTRAST__API__API_KEY;
|
|
50
|
-
delete process.env.CONTRAST__API__SERVICE_KEY;
|
|
51
|
-
delete process.env.CONTRAST__API_USER_NAME;
|
|
52
|
-
const stats = Perf.getStats(allAgentify);
|
|
53
|
-
for (const [key, { n, totalMicros, mean }] of stats.entries()) {
|
|
54
|
-
console.log(key, n, totalMicros, 'nsec', mean, 'mean');
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('invoking with callback will initialize and patch runMain', async function () {
|
|
59
|
-
const core = await agentify(preRunMain, {
|
|
60
|
-
noValidate: true,
|
|
61
|
-
installOrder: ['depHooks'],
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
await Module.runMain();
|
|
65
|
-
|
|
66
|
-
expect(preRunMain).to.have.been.calledWith(core);
|
|
67
|
-
expect(runMain).to.have.been.called;
|
|
68
|
-
expect(depHooksInstall).to.have.been.called;
|
|
69
|
-
expect(loggerError).not.to.have.been.called;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('will not run install methods when installOrder option is empty array', async function () {
|
|
73
|
-
const _core = await agentify(preRunMain, { installOrder: [] });
|
|
74
|
-
await Module.runMain();
|
|
75
|
-
|
|
76
|
-
expect(depHooksInstall).not.called;
|
|
77
|
-
expect(loggerError).not.called;
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
it('handles errors in plugin initialization functions with logger', async function () {
|
|
82
|
-
const err = new Error('oof');
|
|
83
|
-
const agentify = proxyquire('.', {
|
|
84
|
-
'@contrast/logger': {
|
|
85
|
-
default(core) {
|
|
86
|
-
return core.logger = mocks.logger();
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
'@contrast/dep-hooks'() {
|
|
90
|
-
throw err;
|
|
91
|
-
},
|
|
92
|
-
})();
|
|
93
|
-
|
|
94
|
-
const core = await agentify(preRunMain, {
|
|
95
|
-
noValidate: true,
|
|
96
|
-
installOrder: ['depHooks'],
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
await Module.runMain();
|
|
100
|
-
|
|
101
|
-
expect(core).not.to.have.property('depHooks');
|
|
102
|
-
expect(runMain).to.have.been.called;
|
|
103
|
-
expect(core.logger.error).to.have.been.calledWith({ err });
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
it('handles errors in plugin initialization functions without logger', async function () {
|
|
108
|
-
const consoleErrorStub = sinon.stub(console, 'error');
|
|
109
|
-
const err = new Error('boop');
|
|
110
|
-
const agentify = proxyquire('.', {
|
|
111
|
-
'@contrast/logger': {
|
|
112
|
-
default() {
|
|
113
|
-
throw err;
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
'@contrast/dep-hooks'(core) {
|
|
117
|
-
return core.depHooks = mocks.depHooks();
|
|
118
|
-
},
|
|
119
|
-
})();
|
|
120
|
-
|
|
121
|
-
const core = await agentify(preRunMain, {
|
|
122
|
-
noValidate: true,
|
|
123
|
-
installOrder: ['depHooks'],
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
await Module.runMain();
|
|
127
|
-
|
|
128
|
-
expect(core).not.to.have.property('depHooks');
|
|
129
|
-
expect(runMain).to.have.been.called;
|
|
130
|
-
expect(consoleErrorStub).to.have.been.called;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('handles errors in the preRunMain hook', async function () {
|
|
134
|
-
const err = new Error('bonk');
|
|
135
|
-
preRunMain.throws(err);
|
|
136
|
-
const core = await agentify(preRunMain, {
|
|
137
|
-
noValidate: true,
|
|
138
|
-
installOrder: ['depHooks'],
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
await Module.runMain();
|
|
142
|
-
|
|
143
|
-
expect(depHooksInstall).not.to.have.been.called;
|
|
144
|
-
expect(runMain).to.have.been.called;
|
|
145
|
-
expect(core.logger.error).to.have.been.calledWith({ err });
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('handles "unregistered" services', async function () {
|
|
149
|
-
const core = await agentify(preRunMain, {
|
|
150
|
-
noValidate: true,
|
|
151
|
-
installOrder: ['foo'],
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Having non-existent service in list is handled during initialization
|
|
155
|
-
await Module.runMain();
|
|
156
|
-
|
|
157
|
-
expect(core.logger.error).not.to.have.been.called;
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('handles the return value of preRunMain', async function () {
|
|
161
|
-
const install = sinon.stub();
|
|
162
|
-
preRunMain.returns({ install });
|
|
163
|
-
const core = await agentify(preRunMain, { installOrder: [] });
|
|
164
|
-
|
|
165
|
-
// Having non-existent service in list is handled during initialization
|
|
166
|
-
await Module.runMain();
|
|
167
|
-
|
|
168
|
-
expect(install).to.have.been.called;
|
|
169
|
-
expect(core.logger.error).not.to.have.been.called;
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('handles the return value of preRunMain when erroring', async function () {
|
|
173
|
-
const err = new Error('err in install');
|
|
174
|
-
const install = sinon.stub().throws(err);
|
|
175
|
-
preRunMain.returns({ install });
|
|
176
|
-
const core = await agentify(preRunMain, { installOrder: [] });
|
|
177
|
-
|
|
178
|
-
// Having non-existent service in list is handled during initialization
|
|
179
|
-
await Module.runMain();
|
|
180
|
-
|
|
181
|
-
expect(install).to.have.been.called;
|
|
182
|
-
expect(core.logger.error).to.have.been.calledWith({ err });
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('requires init function arg', async function () {
|
|
186
|
-
return expect(agentify('not a function')).to.be.rejectedWith('Invalid usage: first argument must be a function');
|
|
187
|
-
});
|
|
188
|
-
});
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const { expect } = require('chai');
|
|
5
|
-
const { readFileSync } = require('fs');
|
|
6
|
-
const Module = require('module');
|
|
7
|
-
const sinon = require('sinon');
|
|
8
|
-
const mocks = require('@contrast/test/mocks');
|
|
9
|
-
|
|
10
|
-
const testFilePath = require.resolve('../test/resources/file');
|
|
11
|
-
const deadzonedFilePath = require.resolve('../test/node_modules/bcryptjs/dist/bcrypt.js');
|
|
12
|
-
const testFileContent = readFileSync(testFilePath).toString();
|
|
13
|
-
|
|
14
|
-
describe('agentify rewrite-hooks', function () {
|
|
15
|
-
let core, complileSpy, rewriteHooks;
|
|
16
|
-
|
|
17
|
-
beforeEach(function () {
|
|
18
|
-
core = mocks.core();
|
|
19
|
-
core.logger = mocks.logger();
|
|
20
|
-
core.config = mocks.config();
|
|
21
|
-
core.rewriter = mocks.rewriter();
|
|
22
|
-
|
|
23
|
-
complileSpy = sinon.spy(Module.prototype, '_compile');
|
|
24
|
-
|
|
25
|
-
rewriteHooks = require('./rewrite-hooks')(core);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
afterEach(function () {
|
|
29
|
-
delete require.cache[testFilePath];
|
|
30
|
-
rewriteHooks.uninstall();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('rewrites code when the cache returns no results expected', function () {
|
|
34
|
-
core.rewriter.cache.readSync.returns(undefined);
|
|
35
|
-
rewriteHooks.install();
|
|
36
|
-
|
|
37
|
-
require(testFilePath);
|
|
38
|
-
expect(core.rewriter.rewriteSync).to.have.been.calledWith(testFileContent, {
|
|
39
|
-
filename: testFilePath,
|
|
40
|
-
isModule: false,
|
|
41
|
-
inject: true,
|
|
42
|
-
wrap: true,
|
|
43
|
-
});
|
|
44
|
-
expect(core.logger.warn).not.to.have.been.called;
|
|
45
|
-
expect(complileSpy).to.have.been.calledWith(
|
|
46
|
-
core.rewriter.rewriteSync.getCall(0).returnValue.code,
|
|
47
|
-
testFilePath,
|
|
48
|
-
);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('does not not rewrite code when rewrite.enable is false', function () {
|
|
52
|
-
core.config.agent.node.rewrite.enable = false;
|
|
53
|
-
rewriteHooks.install();
|
|
54
|
-
|
|
55
|
-
require(testFilePath);
|
|
56
|
-
expect(core.rewriter.rewriteSync).not.to.have.been.called;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('does not not rewrite code when the cache returns results', function () {
|
|
60
|
-
rewriteHooks.install();
|
|
61
|
-
|
|
62
|
-
require(testFilePath);
|
|
63
|
-
expect(core.rewriter.rewriteSync).not.to.have.been.called;
|
|
64
|
-
expect(complileSpy).to.have.been.calledWith(
|
|
65
|
-
core.rewriter.cache.readSync.getCall(0).returnValue,
|
|
66
|
-
testFilePath,
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('logs a debug message when a file is rewrite-deadzoned', function() {
|
|
71
|
-
rewriteHooks.install();
|
|
72
|
-
|
|
73
|
-
require(deadzonedFilePath);
|
|
74
|
-
|
|
75
|
-
expect(core.logger.debug).calledWith(
|
|
76
|
-
'Skipping rewrite-deadzoned file %s',
|
|
77
|
-
deadzonedFilePath
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('logs a warning if the rewritten module does not compile', function () {
|
|
82
|
-
core.rewriter.cache.readSync.returns(undefined);
|
|
83
|
-
core.rewriter.rewriteSync.callsFake((content) => ({
|
|
84
|
-
code: content.replace('const variable', 'const variable const a'),
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
|
-
rewriteHooks.install();
|
|
88
|
-
|
|
89
|
-
require(testFilePath);
|
|
90
|
-
expect(core.rewriter.rewriteSync).to.have.been.calledWith(testFileContent, {
|
|
91
|
-
filename: testFilePath,
|
|
92
|
-
isModule: false,
|
|
93
|
-
inject: true,
|
|
94
|
-
wrap: true,
|
|
95
|
-
});
|
|
96
|
-
expect(core.logger.warn).to.have.been.calledWith(
|
|
97
|
-
{
|
|
98
|
-
err: sinon.match({
|
|
99
|
-
message: 'Missing initializer in const declaration',
|
|
100
|
-
}),
|
|
101
|
-
},
|
|
102
|
-
'Failed to compile rewritten code for %s, compiling original code.',
|
|
103
|
-
testFilePath,
|
|
104
|
-
);
|
|
105
|
-
expect(complileSpy).to.have.been.calledWith(
|
|
106
|
-
testFileContent,
|
|
107
|
-
testFilePath,
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('throws an error if the original file fails to compile', function () {
|
|
112
|
-
core.rewriter.cache.readSync.returns(undefined);
|
|
113
|
-
const testFilePath = require.resolve('../test/resources/file-bad');
|
|
114
|
-
const testFileContent = readFileSync(testFilePath).toString();
|
|
115
|
-
|
|
116
|
-
rewriteHooks.install();
|
|
117
|
-
|
|
118
|
-
expect(() => require(testFilePath)).to.throw('Unexpected identifier');
|
|
119
|
-
expect(core.rewriter.rewriteSync).to.have.been.calledWith(testFileContent, {
|
|
120
|
-
filename: testFilePath,
|
|
121
|
-
isModule: false,
|
|
122
|
-
inject: true,
|
|
123
|
-
wrap: true,
|
|
124
|
-
});
|
|
125
|
-
expect(core.logger.warn).to.have.been.calledWith(
|
|
126
|
-
{
|
|
127
|
-
err: sinon.match({
|
|
128
|
-
message: sinon.match('Unexpected identifier'),
|
|
129
|
-
}),
|
|
130
|
-
},
|
|
131
|
-
'Failed to compile rewritten code for %s, compiling original code.',
|
|
132
|
-
testFilePath,
|
|
133
|
-
);
|
|
134
|
-
expect(complileSpy).to.have.been.calledWith(
|
|
135
|
-
testFileContent,
|
|
136
|
-
testFilePath,
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
});
|
package/lib/sources.test.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const EventEmitter = require('events');
|
|
4
|
-
const { expect } = require('chai');
|
|
5
|
-
const sinon = require('sinon');
|
|
6
|
-
const { initProtectFixture } = require('@contrast/test/fixtures');
|
|
7
|
-
const proxyquire = require('proxyquire');
|
|
8
|
-
|
|
9
|
-
describe('agentify sources', function () {
|
|
10
|
-
[
|
|
11
|
-
{
|
|
12
|
-
name: 'http',
|
|
13
|
-
expected: {
|
|
14
|
-
port: 8080,
|
|
15
|
-
protocol: 'http',
|
|
16
|
-
serverType: 'http',
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: 'https',
|
|
21
|
-
expected: {
|
|
22
|
-
port: 8080,
|
|
23
|
-
protocol: 'https',
|
|
24
|
-
serverType: 'https',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: 'spdy',
|
|
29
|
-
expected: {
|
|
30
|
-
port: 8080,
|
|
31
|
-
protocol: 'https',
|
|
32
|
-
serverType: 'spdy',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'http2',
|
|
37
|
-
method: 'createServer',
|
|
38
|
-
expected: {
|
|
39
|
-
port: 8080,
|
|
40
|
-
protocol: 'https',
|
|
41
|
-
serverType: 'spdy',
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: 'http2',
|
|
46
|
-
method: 'createSecureServer',
|
|
47
|
-
expected: {
|
|
48
|
-
port: 8080,
|
|
49
|
-
protocol: 'https',
|
|
50
|
-
serverType: 'spdy',
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
].forEach(({ name, method, expected }) => {
|
|
54
|
-
describe(`${name} sources using ${method || 'Server'}()`, function () {
|
|
55
|
-
let core, api, ServerMock, reqMock, resMock, onFinishedMock;
|
|
56
|
-
|
|
57
|
-
beforeEach(function () {
|
|
58
|
-
({ core } = initProtectFixture());
|
|
59
|
-
ServerMock = function ServerMock() {
|
|
60
|
-
this.e = new EventEmitter();
|
|
61
|
-
};
|
|
62
|
-
ServerMock.prototype.emit = function (...args) {
|
|
63
|
-
this.e.emit(...args);
|
|
64
|
-
};
|
|
65
|
-
ServerMock.prototype.on = function (...args) {
|
|
66
|
-
this.e.on(...args);
|
|
67
|
-
};
|
|
68
|
-
api = {
|
|
69
|
-
Server: ServerMock,
|
|
70
|
-
createServer() {
|
|
71
|
-
return new ServerMock();
|
|
72
|
-
},
|
|
73
|
-
createSecureServer() {
|
|
74
|
-
return new ServerMock();
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
reqMock = {
|
|
78
|
-
socket: {
|
|
79
|
-
address() {
|
|
80
|
-
return { port: 8080 };
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
method: 'GET',
|
|
84
|
-
url: 'http://localhost',
|
|
85
|
-
};
|
|
86
|
-
resMock = new EventEmitter();
|
|
87
|
-
onFinishedMock = sinon.stub();
|
|
88
|
-
|
|
89
|
-
core.depHooks.resolve.withArgs(sinon.match({ name: 'http' })).yields(api);
|
|
90
|
-
proxyquire('./sources', {
|
|
91
|
-
'on-finished': onFinishedMock,
|
|
92
|
-
})(core).install();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('"request" events run in scope with correct sourceInfo', function () {
|
|
96
|
-
const server = method ? api[method]() : new ServerMock();
|
|
97
|
-
let store;
|
|
98
|
-
|
|
99
|
-
server.on('request', function () {
|
|
100
|
-
store = core.scopes.sources.getStore();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
server.emit('request', reqMock, resMock);
|
|
104
|
-
|
|
105
|
-
expect(store.sourceInfo).to.deep.include({
|
|
106
|
-
port: 8080,
|
|
107
|
-
protocol: 'http',
|
|
108
|
-
serverType: 'http',
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
expect(onFinishedMock).to.have.been.calledWith(resMock);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('non-"request" events do not run in scope', function () {
|
|
115
|
-
const server = method ? api[method]() : new ServerMock();
|
|
116
|
-
let store;
|
|
117
|
-
|
|
118
|
-
server.on('foo', function () {
|
|
119
|
-
store = core.scopes.sources.getStore();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
server.emit('foo', reqMock, resMock);
|
|
123
|
-
expect(store).to.be.undefined;
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
});
|
package/lib/utils.test.js
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const sinon = require('sinon');
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
assertValidOpts,
|
|
8
|
-
assertSupportedNodeVersion,
|
|
9
|
-
assertSupportedPreloadUsage,
|
|
10
|
-
assertNoExperimentalFeatureFlags,
|
|
11
|
-
} = require('./utils');
|
|
12
|
-
|
|
13
|
-
describe('preStartupValidation', function() {
|
|
14
|
-
|
|
15
|
-
describe('assertValidOpts', function() {
|
|
16
|
-
it('Does not validate when noValidate is true', function() {
|
|
17
|
-
const opts = {
|
|
18
|
-
noValidate: true
|
|
19
|
-
};
|
|
20
|
-
expect(assertValidOpts(opts)).to.be.undefined;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('Throws an error if install order is incorrect', function() {
|
|
24
|
-
const opts = {
|
|
25
|
-
installOrder: [
|
|
26
|
-
'telemetry',
|
|
27
|
-
'reporter',
|
|
28
|
-
'startupValidation'
|
|
29
|
-
]
|
|
30
|
-
};
|
|
31
|
-
expect(() => assertValidOpts(opts)).to.throw('The \'installOrder\' option must include \'reporter\' as first element. found \'telemetry\'');
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('assertSupportedNodeVersion', function() {
|
|
36
|
-
|
|
37
|
-
let engines;
|
|
38
|
-
beforeEach(function() {
|
|
39
|
-
engines = '>=16.9.1 <17 || >=18.7.0 <19 || >=20.6.0 <21 || >= 22.5.1 <23';
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('Does not throw an error if process.version is supported', function() {
|
|
43
|
-
sinon.stub(process, 'version').value('v22.9.0');
|
|
44
|
-
expect(assertSupportedNodeVersion(engines)).to.be.undefined;
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('Throws an error if process.version is unsupported', function() {
|
|
48
|
-
sinon.stub(process, 'version').value('v14.0.0');
|
|
49
|
-
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
|
-
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('assertSupportedPreloadUsage', function() {
|
|
55
|
-
|
|
56
|
-
it('does not throw an err if --import flag is used (>= 18.19.0)', function() {
|
|
57
|
-
sinon.stub(process, 'execArgv').value(['--import', '@contrast/agent']);
|
|
58
|
-
sinon.stub(process, 'version').value('v22.9.0');
|
|
59
|
-
expect(assertSupportedPreloadUsage()).to.be.true;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('throws an err if --import flag is used (<= 18.19.0)', function() {
|
|
63
|
-
sinon.stub(process, 'execArgv').value(['--import', '@contrast/agent']);
|
|
64
|
-
sinon.stub(process, 'version').value('v18.18.0');
|
|
65
|
-
expect(() => assertSupportedPreloadUsage()).to.throw('Contrast requires that Node LTS versions >= 18.19.0 use the --import flag for ESM support');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('throws an err if --loader flag is used (>= 18.19.0)', function() {
|
|
69
|
-
sinon.stub(process, 'execArgv').value(['--loader', '@contrast/agent']);
|
|
70
|
-
sinon.stub(process, 'version').value('v18.20.0');
|
|
71
|
-
expect(() => assertSupportedPreloadUsage()).to.throw('Contrast requires that Node versions less than 18.19.0 use the --loader flag for ESM support');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('assertNoExperimentalFeatureFlags', function() {
|
|
77
|
-
describe('process.execArgv', function() {
|
|
78
|
-
it('Does not throw an error if no experimental feature flag is used', function() {
|
|
79
|
-
sinon.stub(process, 'execArgv').value(['--import', '@contrast/agent']);
|
|
80
|
-
expect(assertNoExperimentalFeatureFlags()).to.be.undefined;
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('Throws an error if an experimental feature flag is used', function() {
|
|
84
|
-
sinon.stub(process, 'execArgv').value(['--experimental-feature', '--import', '@contrast/agent']);
|
|
85
|
-
expect(() => assertNoExperimentalFeatureFlags()).to.throw('Contrast Agent does not support experimental features.');
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('NODE_OPTIONS', function() {
|
|
90
|
-
|
|
91
|
-
const origNodeOptions = process.env?.NODE_OPTIONS;
|
|
92
|
-
|
|
93
|
-
afterEach(function() {
|
|
94
|
-
process.env.NODE_OPTIONS = origNodeOptions;
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('Does not throw an error if no experimental feature flag is used', function() {
|
|
98
|
-
process.env.NODE_OPTIONS = '--import @contrast/agent';
|
|
99
|
-
expect(assertNoExperimentalFeatureFlags()).to.be.undefined;
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('Throws an error if an experimental feature flag is used', function() {
|
|
103
|
-
process.env.NODE_OPTIONS = '--experimental-feature --import @contrast/agent';
|
|
104
|
-
expect(() => assertNoExperimentalFeatureFlags()).to.throw('Contrast Agent does not support experimental features.');
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
});
|
package/lib/validators.test.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const assert = require('node:assert');
|
|
4
|
-
const sinon = require('sinon');
|
|
5
|
-
const mocks = require('@contrast/test/mocks');
|
|
6
|
-
const { IntentionalError } = require('@contrast/common');
|
|
7
|
-
|
|
8
|
-
const originalArgv = process.argv;
|
|
9
|
-
const originalConsoleWarn = console.warn;
|
|
10
|
-
const originalConsoleError = console.error;
|
|
11
|
-
|
|
12
|
-
describe('reporter validators: config', function() {
|
|
13
|
-
let getEffectiveValue;
|
|
14
|
-
let logger;
|
|
15
|
-
let tcore;
|
|
16
|
-
let validators;
|
|
17
|
-
|
|
18
|
-
beforeEach(function() {
|
|
19
|
-
// change this to test different scenarios
|
|
20
|
-
getEffectiveValue = function() {
|
|
21
|
-
return false;
|
|
22
|
-
};
|
|
23
|
-
logger = mocks.logger();
|
|
24
|
-
tcore = {
|
|
25
|
-
config: {
|
|
26
|
-
enable: true,
|
|
27
|
-
getEffectiveValue: (prop) => getEffectiveValue(prop)
|
|
28
|
-
},
|
|
29
|
-
logger,
|
|
30
|
-
};
|
|
31
|
-
validators = require('@contrast/agentify/lib/validators.js');
|
|
32
|
-
|
|
33
|
-
process.argv = originalArgv.slice();
|
|
34
|
-
console.warn = sinon.stub();
|
|
35
|
-
console.error = sinon.stub();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
after(function() {
|
|
39
|
-
console.warn = originalConsoleWarn;
|
|
40
|
-
console.error = originalConsoleError;
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('throws IntentionalError (info) when config enable: false', async function() {
|
|
44
|
-
tcore.config.enable = false;
|
|
45
|
-
|
|
46
|
-
assert.throws(() => validators.config(tcore), (e) => {
|
|
47
|
-
assert(logger.warn.notCalled);
|
|
48
|
-
assert(e instanceof IntentionalError);
|
|
49
|
-
assert.match(e.message, /Contrast agent disabled by configuration/);
|
|
50
|
-
return true;
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
['-c', '--configFile'].forEach(function(tflag) {
|
|
55
|
-
['Contrast_Security.yml', 'contrast-security.yaml', 'app.cfg'].forEach(function(farg) {
|
|
56
|
-
const shouldWarn = farg !== 'app.cfg';
|
|
57
|
-
const msg = shouldWarn ? `logs ${tflag} ${farg}` : `does not log ${tflag} ${farg}`;
|
|
58
|
-
|
|
59
|
-
it(`${msg} before an Error is thrown`, async function() {
|
|
60
|
-
tcore.config.enable = false;
|
|
61
|
-
process.argv.push(tflag, farg);
|
|
62
|
-
|
|
63
|
-
assert.throws(() => validators.config(tcore), (e) => {
|
|
64
|
-
if (shouldWarn) {
|
|
65
|
-
assert(logger.warn.calledOnceWith(`Command line config flag is not supported: ${tflag} ${farg}`));
|
|
66
|
-
} else {
|
|
67
|
-
assert(logger.warn.notCalled);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
assert(e instanceof Error);
|
|
71
|
-
assert.match(e.message, /Contrast agent disabled by configuration/);
|
|
72
|
-
return true;
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|