@contrast/agent 4.31.1 → 4.32.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/bin/VERSION CHANGED
@@ -1 +1 @@
1
- 2.28.23
1
+ 2.28.24
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -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;
@@ -191,6 +191,18 @@ function getAgentArgs(options) {
191
191
  return agentArgs;
192
192
  }
193
193
 
194
+ function parseArgv(argv) {
195
+ const parsed = [];
196
+ const cwd = process.cwd();
197
+ for (let i = 0; i < argv.length; i++) {
198
+ if (argv[i].includes(cwd)) {
199
+ parsed.push(path.relative(cwd, argv[i]).toLowerCase());
200
+ } else {
201
+ parsed.push(path.basename(argv[i]).toLowerCase());
202
+ }
203
+ }
204
+ return parsed;
205
+ }
194
206
  /**
195
207
  * Initializes config, logger and other non-instrumentation code.
196
208
  *
@@ -210,6 +222,26 @@ contrastAgent.prepare = function(...args) {
210
222
  const configUtil = require('./core/config/util');
211
223
  const config = configUtil.setup(options, logger);
212
224
 
225
+ if (config.agent.node.exclusive_entrypoint) {
226
+ const parsed = parseArgv(process.argv);
227
+ if (!parsed.includes(config.agent.node.exclusive_entrypoint)) {
228
+ return false;
229
+ }
230
+ }
231
+
232
+ if (config.agent.node.cmd_ignore_list) {
233
+ const parsed = parseArgv(process.argv);
234
+ const cmdIgnoreList = config.agent.node.cmd_ignore_list.split(',');
235
+ if (parsed.includes('npm')) {
236
+ if (cmdIgnoreList.includes('npm*')) return false;
237
+ parsed.shift();
238
+ }
239
+ const cmd = parsed.join(' ');
240
+ if (cmdIgnoreList.includes(cmd)) {
241
+ return false;
242
+ }
243
+ }
244
+
213
245
  loggerFactory.init(config);
214
246
 
215
247
  contrastAgent.doImports();
@@ -454,6 +454,18 @@ const agent = [
454
454
  default: 16,
455
455
  desc: 'set the maximum body size that will be sent to speedracer for input analysis',
456
456
  },
457
+ {
458
+ // SEE NODE-2886
459
+ name: 'agent.node.cmd_ignore_list',
460
+ arg: '<commands>',
461
+ desc: 'comma-separated list of commands that will not startup the agent if agent is required; npm* will ignore all npm executables but not your application\'s scripts'
462
+ },
463
+ {
464
+ // SEE NODE-2886
465
+ name: 'agent.node.exclusive_entrypoint',
466
+ arg: '<entrypoint.js>',
467
+ desc: 'an entrypoint for an application that, when specified, will prevent the agent instrumenting on anything else'
468
+ },
457
469
  {
458
470
  name: 'agent.heap_dump.enable',
459
471
  arg: '[true]',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agent",
3
- "version": "4.31.1",
3
+ "version": "4.32.0",
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",