@contrast/agent 4.31.0 → 4.31.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.
- package/bin/VERSION +1 -1
- package/bin/contrast-service-darwin-arm64 +0 -0
- package/bin/contrast-service-darwin-x64 +0 -0
- package/bin/contrast-service-linux-arm64 +0 -0
- package/bin/contrast-service-linux-x64 +0 -0
- package/bin/contrast-service-win32-x64.exe +0 -0
- package/config-diagnostics.js +1 -2
- package/lib/assess/fastify/sinks/xss.js +2 -3
- package/lib/assess/hapi/sinks/xss.js +12 -1
- package/lib/assess/models/base-event.js +15 -8
- package/lib/contrast.js +15 -17
- package/lib/core/config/options.js +21 -1
- package/lib/core/config/util.js +14 -3
- package/lib/core/fastify/index.js +1 -1
- package/lib/util/config-diagnostics-utils.js +6 -9
- package/package.json +2 -2
- package/system-diagnostics.js +1 -1
package/bin/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.28.
|
|
1
|
+
2.28.24
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/config-diagnostics.js
CHANGED
|
@@ -77,8 +77,7 @@ const diagnostics = {
|
|
|
77
77
|
executeNodeAgent(args) {
|
|
78
78
|
let agentEnvArgs =
|
|
79
79
|
'CONTRAST__SHOW__BANNER=false ' +
|
|
80
|
-
'CONTRAST__AGENT__DIAGNOSTICS__ENABLE=true '
|
|
81
|
-
'CONTRAST__AGENT__SYSTEM_DIAGNOSTICS__ENABLE=false';
|
|
80
|
+
'CONTRAST__AGENT__DIAGNOSTICS__ENABLE=true ';
|
|
82
81
|
|
|
83
82
|
if (!args.quiet) {
|
|
84
83
|
agentEnvArgs = `${agentEnvArgs} CONTRAST__AGENT__DIAGNOSTICS__QUIET=false`;
|
|
@@ -196,7 +196,8 @@ class FastifyXssSink {
|
|
|
196
196
|
alwaysRun: true,
|
|
197
197
|
pre(data) {
|
|
198
198
|
// Final check after handler and all hooks have ran
|
|
199
|
-
const
|
|
199
|
+
const replyState = AsyncStorage.get(KEYS.FASTIFY_REPLY_SEND_STATE);
|
|
200
|
+
const payload = data.args[0] || replyState.payload;
|
|
200
201
|
|
|
201
202
|
if (
|
|
202
203
|
isVulnerable({
|
|
@@ -206,8 +207,6 @@ class FastifyXssSink {
|
|
|
206
207
|
ruleId
|
|
207
208
|
})
|
|
208
209
|
) {
|
|
209
|
-
const replyState = AsyncStorage.get(KEYS.FASTIFY_REPLY_SEND_STATE);
|
|
210
|
-
// If the handler called Reply.send with a vuln report that stack. Otherwise just report the current stack
|
|
211
210
|
const ctxt = replyState
|
|
212
211
|
? CallContext.create({
|
|
213
212
|
obj: replyState.reply,
|
|
@@ -118,6 +118,14 @@ class HapiXssSink {
|
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
hasViewEngineSource(source) {
|
|
122
|
+
if (typeof source === 'object' && source.manager && source.context) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
121
129
|
/**
|
|
122
130
|
* Checks the response in a `onPreResponse` hook for xss vulns
|
|
123
131
|
*/
|
|
@@ -140,11 +148,14 @@ class HapiXssSink {
|
|
|
140
148
|
requiredTags
|
|
141
149
|
} = this.common;
|
|
142
150
|
|
|
151
|
+
const input = this.hasViewEngineSource(source) ? source.context : source;
|
|
152
|
+
|
|
143
153
|
if (
|
|
144
154
|
isVulnerable({
|
|
145
|
-
input
|
|
155
|
+
input,
|
|
146
156
|
disallowedTags,
|
|
147
157
|
requiredTags,
|
|
158
|
+
searchDepth: typeof input === 'string' ? 0 : Infinity,
|
|
148
159
|
ruleId
|
|
149
160
|
})
|
|
150
161
|
) {
|
|
@@ -82,12 +82,19 @@ class BaseEvent {
|
|
|
82
82
|
getAllParents(set = new Set()) {
|
|
83
83
|
this.parents = sortEvents(this.parents);
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
try {
|
|
86
|
+
this.parents.forEach((p) => {
|
|
87
|
+
if (p && !set.has(p)) {
|
|
88
|
+
set = p.getAllParents(set);
|
|
89
|
+
set.add(p);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
} catch (err) {
|
|
93
|
+
logger.warn(
|
|
94
|
+
'Unable to get all parents for dataflowEvent',
|
|
95
|
+
err
|
|
96
|
+
);
|
|
97
|
+
}
|
|
91
98
|
|
|
92
99
|
return set;
|
|
93
100
|
}
|
|
@@ -114,14 +121,14 @@ class BaseEvent {
|
|
|
114
121
|
if (this.source === 'P') {
|
|
115
122
|
const numArgs = this.context.hasArgsTracked.length;
|
|
116
123
|
if (numArgs === 1) {
|
|
117
|
-
this.source = 'P0'
|
|
124
|
+
this.source = 'P0';
|
|
118
125
|
} else {
|
|
119
126
|
this.source = '';
|
|
120
127
|
for (let i = 0; i < numArgs; i++) {
|
|
121
128
|
if (this.context.hasArgsTracked[i]) {
|
|
122
129
|
this.source += `P${i},`;
|
|
123
130
|
}
|
|
124
|
-
}
|
|
131
|
+
}
|
|
125
132
|
}
|
|
126
133
|
}
|
|
127
134
|
|
package/lib/contrast.js
CHANGED
|
@@ -115,16 +115,16 @@ contrastAgent.checkNodeVersion = function(
|
|
|
115
115
|
!semver.satisfies(version, supportedVersions) ||
|
|
116
116
|
semver.major(version) % 2
|
|
117
117
|
) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
.
|
|
121
|
-
.pop()
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
let validRanges = '';
|
|
119
|
+
supportedVersions.split('||').forEach((range) => {
|
|
120
|
+
const minVersion = semver.minVersion(range).toString();
|
|
121
|
+
const maxVersion = range.split('<').pop()
|
|
122
|
+
.trim();
|
|
123
|
+
validRanges += `${minVersion} and ${maxVersion}, `;
|
|
124
|
+
});
|
|
124
125
|
logger.error(
|
|
125
|
-
'Contrast only officially supports Node LTS versions between %
|
|
126
|
-
|
|
127
|
-
maxVersion,
|
|
126
|
+
'Contrast only officially supports Node LTS versions between %sbut detected %s. Continuing without instrumentation.',
|
|
127
|
+
validRanges,
|
|
128
128
|
version
|
|
129
129
|
);
|
|
130
130
|
return false;
|
|
@@ -319,8 +319,8 @@ contrastAgent.bootstrap = function(args) {
|
|
|
319
319
|
);
|
|
320
320
|
})
|
|
321
321
|
.finally(async () => {
|
|
322
|
-
if (
|
|
323
|
-
let destination =
|
|
322
|
+
if (agent.config._flat['agent.diagnostics.enable'] !== false) {
|
|
323
|
+
let destination = agent.config._flat['agent.diagnostics.report_path'];
|
|
324
324
|
if (destination == null) {
|
|
325
325
|
destination = agent.config._flat['agent.logger.path'].split('/');
|
|
326
326
|
destination.pop() && destination.push('contrast_effective_config.json');
|
|
@@ -328,7 +328,7 @@ contrastAgent.bootstrap = function(args) {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
const args = {
|
|
331
|
-
quiet:
|
|
331
|
+
quiet: agent.config._flat['agent.diagnostics.quiet'],
|
|
332
332
|
output: destination,
|
|
333
333
|
skip: false,
|
|
334
334
|
};
|
|
@@ -339,11 +339,9 @@ contrastAgent.bootstrap = function(args) {
|
|
|
339
339
|
outputAgentConfigFile(agent, options, args, err);
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
outputSystemInfo(args, info);
|
|
346
|
-
}
|
|
342
|
+
args.output = path.join(args.output, '../contrast_system_info.json');
|
|
343
|
+
const info = fetchSystemInfo();
|
|
344
|
+
outputSystemInfo(args, info);
|
|
347
345
|
}
|
|
348
346
|
});
|
|
349
347
|
};
|
|
@@ -240,6 +240,25 @@ const api = [
|
|
|
240
240
|
];
|
|
241
241
|
|
|
242
242
|
const agent = [
|
|
243
|
+
{
|
|
244
|
+
name: 'agent.diagnostics.enable',
|
|
245
|
+
arg: '[false]',
|
|
246
|
+
default: true,
|
|
247
|
+
fn: castBoolean,
|
|
248
|
+
desc: 'If true the agent will try to create both diagnostic files at startup',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'agent.diagnostics.quiet',
|
|
252
|
+
arg: '[false]',
|
|
253
|
+
default: true,
|
|
254
|
+
fn: castBoolean,
|
|
255
|
+
desc: 'If true the agent will print all diagnostic results to stdout as well',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'agent.diagnostics.report_path',
|
|
259
|
+
arg: '<path>',
|
|
260
|
+
desc: 'path indicating where to report all diagnostics results',
|
|
261
|
+
},
|
|
243
262
|
{
|
|
244
263
|
name: 'agent.logger.append',
|
|
245
264
|
arg: '[false]',
|
|
@@ -724,7 +743,8 @@ const assess = [
|
|
|
724
743
|
{
|
|
725
744
|
name: 'assess.enable_sanitizer_serve_static',
|
|
726
745
|
arg: '<enable-sanitizer-serve-static>',
|
|
727
|
-
default:
|
|
746
|
+
default: true,
|
|
747
|
+
fn: castBoolean,
|
|
728
748
|
desc: 'Enables the serve-static module as a path-traversal sanitizer. Express uses serve-static in a safe way' +
|
|
729
749
|
'but manual setup of serve-static can be vulnerable. Even with Express there is a possibility for "traversing-down"' +
|
|
730
750
|
'the served folder or user misconfiguration if not configured with an absolute path',
|
package/lib/core/config/util.js
CHANGED
|
@@ -301,10 +301,21 @@ function mergeCliOptions(cliOptions, logger) {
|
|
|
301
301
|
// if it's an enum, find it in the enum or set the value to default
|
|
302
302
|
// ineffective if optDefault wasn't in the enum;
|
|
303
303
|
// optDefault won't get passed through fn, so it needs to be valid.
|
|
304
|
-
// XXX: do we want to warn whenever we fallback? that's tricky because
|
|
305
|
-
// logger hasn't been initialized yet.
|
|
306
304
|
if (optEnum && optEnum.indexOf(value) === -1) {
|
|
307
|
-
|
|
305
|
+
const defaultOption = fn(optDefault, logger);
|
|
306
|
+
// If value is set but not in optEnum
|
|
307
|
+
if (value) {
|
|
308
|
+
logger.error(
|
|
309
|
+
"'%s' is not a valid option for %s\n\
|
|
310
|
+
Valid options include: %s\n\
|
|
311
|
+
Setting to default option: '%s'",
|
|
312
|
+
value,
|
|
313
|
+
name,
|
|
314
|
+
optEnum,
|
|
315
|
+
defaultOption
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
value = defaultOption;
|
|
308
319
|
isFromDefault = true;
|
|
309
320
|
origin = 'DEFAULT';
|
|
310
321
|
}
|
|
@@ -32,7 +32,7 @@ const constants = {
|
|
|
32
32
|
FASTIFY_EXPORT: 'fastify-export', // used to patch methods on the fastify framework
|
|
33
33
|
PRE_VALIDATION: 'fastify-prevalidation-hook', // used to implement fastify `preValidation` hook
|
|
34
34
|
ON_SEND: 'fastify-onsend-hook', // used to implement fastify `onSend` hook
|
|
35
|
-
ON_ROUTE: 'fastify-onroute-hook' // used to implement fastify `onRoute` hook
|
|
35
|
+
ON_ROUTE: 'fastify-onroute-hook', // used to implement fastify `onRoute` hook
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
|
|
@@ -206,19 +206,16 @@ function outputAgentConfigFile(agent, options, args, err) {
|
|
|
206
206
|
fs.accessSync(path.join(args.output, '..'), fs.constants.RDWD_OK);
|
|
207
207
|
fs.writeFileSync(args.output, JSON.stringify(effectiveConfig, null, 2), 'utf-8');
|
|
208
208
|
} catch (err) {
|
|
209
|
-
// try to write the file at pwd instead
|
|
210
209
|
args.output = path.join(process.cwd(), 'contrast_effective_config.json');
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
} catch (err) {
|
|
217
|
-
console.log(`Couldn't create effective config file: ${err}`);
|
|
210
|
+
try {
|
|
211
|
+
fs.writeFileSync(args.output, JSON.stringify(effectiveConfig, null, 2), 'utf-8');
|
|
212
|
+
} catch (err) {
|
|
213
|
+
console.log(`Couldn't create effective config file: ${err}`);
|
|
214
|
+
}
|
|
218
215
|
}
|
|
219
216
|
|
|
220
217
|
if (!args.quiet) {
|
|
221
|
-
|
|
218
|
+
fs.writeFileSync(1, JSON.stringify(effectiveConfig, null, 2), 'utf-8');
|
|
222
219
|
}
|
|
223
220
|
}
|
|
224
221
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agent",
|
|
3
|
-
"version": "4.31.
|
|
3
|
+
"version": "4.31.2",
|
|
4
4
|
"description": "Node.js security instrumentation by Contrast Security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"security",
|
|
@@ -181,7 +181,7 @@
|
|
|
181
181
|
"proxyquire": "^2.1.0",
|
|
182
182
|
"qs": "^6.9.4",
|
|
183
183
|
"rethinkdb": "file:test/mock/rethinkdb",
|
|
184
|
-
"sequelize": "^6.
|
|
184
|
+
"sequelize": "^6.29.0",
|
|
185
185
|
"serve-static": "^1.15.0",
|
|
186
186
|
"shellcheck": "^1.0.0",
|
|
187
187
|
"sinon": "^9.2.4",
|