@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 CHANGED
@@ -1 +1 @@
1
- 2.28.23
1
+ 2.28.24
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -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 payload = data.args[0];
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: source,
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
- this.parents.forEach((p) => {
86
- if (p && !set.has(p)) {
87
- set = p.getAllParents(set);
88
- set.add(p);
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
- const minVersion = semver.minVersion(supportedVersions).toString();
119
- const maxVersion = supportedVersions
120
- .split('||')
121
- .pop()
122
- .split('<')
123
- .pop();
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 %s and %s but detected %s. Continuing without instrumentation.',
126
- minVersion,
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 (!(process.env['CONTRAST__AGENT__DIAGNOSTICS__ENABLE'] === 'false')) {
323
- let destination = process.env['CONTRAST__AGENT__DIAGNOSTICS__REPORT_PATH'];
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: (process.env['CONTRAST__AGENT__DIAGNOSTICS__QUIET'] === 'false') ? false : true,
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
- if (!(process.env['CONTRAST__AGENT__SYSTEM_DIAGNOSTICS__ENABLE'] === 'false')) {
343
- args.output = path.join(args.output, '../contrast_system_info.json');
344
- const info = fetchSystemInfo();
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: false,
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',
@@ -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
- value = fn(optDefault, logger);
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
- try {
214
- fs.accessSync(path.join(args.output, '..'), fs.constants.RDWD_OK);
215
- fs.writeFileSync(args.output, JSON.stringify(effectiveConfig, null, 2), 'utf-8');
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
- console.log(JSON.stringify(effectiveConfig, null, 2));
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.0",
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.11.0",
184
+ "sequelize": "^6.29.0",
185
185
  "serve-static": "^1.15.0",
186
186
  "shellcheck": "^1.0.0",
187
187
  "sinon": "^9.2.4",
@@ -132,7 +132,7 @@ const diagnostics = {
132
132
  }
133
133
 
134
134
  if (!args.quiet) {
135
- console.log(JSON.stringify(info, null, 2));
135
+ fs.writeFileSync(1, JSON.stringify(info, null, 2), 'utf-8');
136
136
  }
137
137
  },
138
138