@contrast/agent 4.25.5 → 4.26.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.
|
@@ -21,6 +21,7 @@ const stackFactory = require('../../core/stacktrace').singleton;
|
|
|
21
21
|
const { PROXY_TARGET } = require('../../../lib/constants');
|
|
22
22
|
const TagRange = require('../models/tag-range');
|
|
23
23
|
const distringuish = require('@contrast/distringuish');
|
|
24
|
+
const agent = require('../../agent');
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Holds information about the call context of a function
|
|
@@ -153,29 +154,36 @@ function valueString(value) {
|
|
|
153
154
|
return type;
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
|
|
157
|
+
const appendStack = agent.config.assess.enable_lazy_stacktraces ?
|
|
158
|
+
(instance, snapshot) => {
|
|
159
|
+
let stack;
|
|
160
|
+
// This enables the lazy generation of a stacktrace which is a performance
|
|
161
|
+
// improvement, but the snapshot method call is in a closure which creates
|
|
162
|
+
// a memory leak in longer running tests
|
|
163
|
+
Object.defineProperty(instance, 'stack', {
|
|
164
|
+
enumerable: true,
|
|
165
|
+
configurable: true,
|
|
166
|
+
get() {
|
|
167
|
+
if (!stack) {
|
|
168
|
+
stack = snapshot();
|
|
169
|
+
}
|
|
170
|
+
return stack;
|
|
171
|
+
},
|
|
172
|
+
set(value) {
|
|
173
|
+
stack = value;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
} : (instance, snapshot) => {
|
|
177
|
+
instance.stack = snapshot();
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
function create (params) {
|
|
157
181
|
const instance = new CallContext(params);
|
|
158
182
|
const snapshot = params.stacktrace || stackFactory.createSnapshot({
|
|
159
183
|
constructorOpt: params.hooked
|
|
160
184
|
});
|
|
161
185
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// If this occurs within the constructor it will leak all references within
|
|
165
|
-
// the snapshot's closure.
|
|
166
|
-
Object.defineProperty(instance, 'stack', {
|
|
167
|
-
enumerable: true,
|
|
168
|
-
configurable: true,
|
|
169
|
-
get() {
|
|
170
|
-
if (!stack) {
|
|
171
|
-
stack = snapshot();
|
|
172
|
-
}
|
|
173
|
-
return stack;
|
|
174
|
-
},
|
|
175
|
-
set(value) {
|
|
176
|
-
stack = value;
|
|
177
|
-
}
|
|
178
|
-
});
|
|
186
|
+
appendStack(instance, snapshot);
|
|
179
187
|
|
|
180
188
|
return instance;
|
|
181
189
|
}
|
|
@@ -12,6 +12,8 @@ Copyright: 2022 Contrast Security, Inc
|
|
|
12
12
|
engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
13
|
way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
15
17
|
const CallContext = require('./call-context');
|
|
16
18
|
const Signature = require('./signature');
|
|
17
19
|
const BaseEvent = require('./base-event');
|
|
@@ -761,6 +761,13 @@ const assess = [
|
|
|
761
761
|
fn: castBoolean,
|
|
762
762
|
desc: 'When set to `false` won\'t track the source events lazily but will track the first up to 250 source events',
|
|
763
763
|
},
|
|
764
|
+
{
|
|
765
|
+
name: 'assess.enable_lazy_stacktraces',
|
|
766
|
+
arg: '[false]',
|
|
767
|
+
default: false,
|
|
768
|
+
fn: castBoolean,
|
|
769
|
+
desc: 'When set to `true` it will evaluate stacktraces lazily, this improves performance, but causes a memory leak if used in a longer running application',
|
|
770
|
+
},
|
|
764
771
|
];
|
|
765
772
|
|
|
766
773
|
const protect = [
|
package/lib/core/config/util.js
CHANGED
|
@@ -37,7 +37,7 @@ const util = module.exports;
|
|
|
37
37
|
* @param {*} value
|
|
38
38
|
* @param {boolean} def set from default or not
|
|
39
39
|
*/
|
|
40
|
-
function set(conf, name, value, def) {
|
|
40
|
+
function set(conf, name, value, def, origin) {
|
|
41
41
|
// use _.set so we can nest properties for names like 'application.vulnerability.tags'
|
|
42
42
|
// and ensure 'var t = config.application.vulnerability.tags' is not going
|
|
43
43
|
// to throw anything like 'can't read property tagRanges of undefined'
|
|
@@ -47,6 +47,7 @@ function set(conf, name, value, def) {
|
|
|
47
47
|
if (!name.startsWith('api.')) {
|
|
48
48
|
conf._flat[name] = value;
|
|
49
49
|
}
|
|
50
|
+
conf._sources[name] = origin;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
class ConfigurationError extends Error {
|
|
@@ -59,6 +60,7 @@ class Config {
|
|
|
59
60
|
constructor() {
|
|
60
61
|
this._default = {};
|
|
61
62
|
this._flat = {};
|
|
63
|
+
this._sources = {};
|
|
62
64
|
|
|
63
65
|
// all properties on node.agent should always exist, so that
|
|
64
66
|
// no code needs to check whether they exist or not. this rule
|
|
@@ -87,6 +89,10 @@ class Config {
|
|
|
87
89
|
return this._default[name];
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
getOrigin(name) {
|
|
93
|
+
return this._sources[name];
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
query(name) {
|
|
91
97
|
const isDefault = this.isDefault(name);
|
|
92
98
|
const value = this.get(name);
|
|
@@ -188,9 +194,8 @@ function readConfig(cliOptions, logger) {
|
|
|
188
194
|
let fileContents;
|
|
189
195
|
|
|
190
196
|
if (configPath) {
|
|
191
|
-
cliOptions.configFile = path.resolve(configPath);
|
|
192
197
|
try {
|
|
193
|
-
fileContents = fs.readFileSync(
|
|
198
|
+
fileContents = fs.readFileSync(path.resolve(configPath), 'utf-8');
|
|
194
199
|
} catch (e) {
|
|
195
200
|
logger.error(`Unable to read config file. ${e.message}`);
|
|
196
201
|
}
|
|
@@ -211,7 +216,7 @@ function readConfig(cliOptions, logger) {
|
|
|
211
216
|
}
|
|
212
217
|
}
|
|
213
218
|
|
|
214
|
-
return config;
|
|
219
|
+
return { config, configPath };
|
|
215
220
|
}
|
|
216
221
|
|
|
217
222
|
/**
|
|
@@ -221,10 +226,9 @@ function readConfig(cliOptions, logger) {
|
|
|
221
226
|
* @param {Object} cliOptions
|
|
222
227
|
* @return {Object} "un-flattened" file options
|
|
223
228
|
*/
|
|
224
|
-
function
|
|
225
|
-
const config = readConfig(cliOptions, logger);
|
|
226
|
-
return _.reduce(
|
|
227
|
-
config,
|
|
229
|
+
util.getFileOptions = function(cliOptions, logger) {
|
|
230
|
+
const { config, configPath } = readConfig(cliOptions, logger);
|
|
231
|
+
return _.reduce({ ...config, configPath },
|
|
228
232
|
(memo, value, key) => {
|
|
229
233
|
// Merge if necessary,
|
|
230
234
|
if (memo[key]) {
|
|
@@ -233,12 +237,11 @@ function getFileOptions(cliOptions, logger) {
|
|
|
233
237
|
// but otherwise set the config path.
|
|
234
238
|
_.set(memo, key, value);
|
|
235
239
|
}
|
|
236
|
-
|
|
237
240
|
return memo;
|
|
238
241
|
},
|
|
239
242
|
{}
|
|
240
243
|
);
|
|
241
|
-
}
|
|
244
|
+
};
|
|
242
245
|
|
|
243
246
|
/**
|
|
244
247
|
* only set autoEnv if it's a common config option
|
|
@@ -256,8 +259,9 @@ function getAutoEnv(name) {
|
|
|
256
259
|
* @return {Config} merged options
|
|
257
260
|
*/
|
|
258
261
|
function mergeCliOptions(cliOptions, logger) {
|
|
259
|
-
const fileOptions = getFileOptions(cliOptions, logger);
|
|
262
|
+
const fileOptions = util.getFileOptions(cliOptions, logger);
|
|
260
263
|
|
|
264
|
+
// eslint-disable-next-line complexity
|
|
261
265
|
return configOptions.reduce((options, option) => {
|
|
262
266
|
const {
|
|
263
267
|
default: optDefault,
|
|
@@ -273,12 +277,26 @@ function mergeCliOptions(cliOptions, logger) {
|
|
|
273
277
|
const fileFlag = _.get(fileOptions, name);
|
|
274
278
|
|
|
275
279
|
// For some values, we want to know if we assigned by falling back to default
|
|
276
|
-
let isFromDefault;
|
|
277
|
-
|
|
278
280
|
// cli > env > file > default
|
|
281
|
+
let isFromDefault, origin;
|
|
282
|
+
|
|
283
|
+
if (cliFlag != null) {
|
|
284
|
+
origin = 'CLI';
|
|
285
|
+
} else if (env != null || autoEnv != null) {
|
|
286
|
+
origin = 'ENV';
|
|
287
|
+
} else if (fileFlag != null) {
|
|
288
|
+
origin = 'YAML';
|
|
289
|
+
}
|
|
290
|
+
|
|
279
291
|
let value = [cliFlag, env, autoEnv, fileFlag]
|
|
280
|
-
.map((v) => fn(v, logger))
|
|
281
|
-
|
|
292
|
+
.map((v) => fn(v, logger)).find((flag) => flag !== undefined);
|
|
293
|
+
|
|
294
|
+
// This is kind of a special case. We never set a default value for it so
|
|
295
|
+
// if we want to see it in the config, it needs to be set explicitly
|
|
296
|
+
if (name == 'configFile' && value == null) {
|
|
297
|
+
value = fileOptions.configPath;
|
|
298
|
+
origin = 'DEFAULT';
|
|
299
|
+
}
|
|
282
300
|
|
|
283
301
|
// if it's an enum, find it in the enum or set the value to default
|
|
284
302
|
// ineffective if optDefault wasn't in the enum;
|
|
@@ -288,6 +306,7 @@ function mergeCliOptions(cliOptions, logger) {
|
|
|
288
306
|
if (optEnum && optEnum.indexOf(value) === -1) {
|
|
289
307
|
value = fn(optDefault, logger);
|
|
290
308
|
isFromDefault = true;
|
|
309
|
+
origin = 'DEFAULT';
|
|
291
310
|
}
|
|
292
311
|
|
|
293
312
|
// set default last and separately, so that we can mark that the option was
|
|
@@ -297,12 +316,12 @@ function mergeCliOptions(cliOptions, logger) {
|
|
|
297
316
|
logger.error("Missing required option '%s'", name);
|
|
298
317
|
return options;
|
|
299
318
|
}
|
|
300
|
-
|
|
301
319
|
value = fn(optDefault, logger);
|
|
302
320
|
isFromDefault = true;
|
|
321
|
+
origin = 'DEFAULT';
|
|
303
322
|
}
|
|
323
|
+
set(options, name, value, isFromDefault, origin);
|
|
304
324
|
|
|
305
|
-
set(options, name, value, isFromDefault);
|
|
306
325
|
return options;
|
|
307
326
|
}, new Config());
|
|
308
327
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agent",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.26.0",
|
|
4
4
|
"description": "Node.js security instrumentation by Contrast Security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"dustjs-linkedin": "^3.0.1",
|
|
139
139
|
"ejs": "^3.1.7",
|
|
140
140
|
"escape-html": "^1.0.3",
|
|
141
|
-
"eslint": "^8.
|
|
141
|
+
"eslint": "^8.26.0",
|
|
142
142
|
"eslint-plugin-mocha": "^10.0.3",
|
|
143
143
|
"eslint-plugin-node": "^11.1.0",
|
|
144
144
|
"express": "file:test/mock/express",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"lint-staged": "^12.0.2",
|
|
159
159
|
"madge": "^4.0.1",
|
|
160
160
|
"marsdb": "file:test/mock/marsdb",
|
|
161
|
-
"mocha": "^9.2.
|
|
161
|
+
"mocha": "^9.2.2",
|
|
162
162
|
"mochawesome": "^7.0.1",
|
|
163
163
|
"mock-fs": "^5.1.2",
|
|
164
164
|
"mongodb": "file:test/mock/mongodb",
|