postman-runtime 7.28.0 → 7.28.4

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.
@@ -7,15 +7,22 @@ var _ = require('lodash'),
7
7
  *
8
8
  * @constructs AuthInterface
9
9
  * @param {RequestAuth} auth
10
+ * @param {Object} protocolProfileBehavior - Protocol profile behaviors
10
11
  * @return {AuthInterface}
11
12
  * @throws {Error}
12
13
  */
13
- createAuthInterface = function (auth) {
14
+ createAuthInterface = function (auth, protocolProfileBehavior) {
14
15
  if (!(auth && auth.parameters && auth.parameters())) {
15
16
  throw new Error('runtime~createAuthInterface: invalid auth');
16
17
  }
17
18
 
18
19
  return /** @lends AuthInterface.prototype **/{
20
+ /**
21
+ * @private
22
+ * @property {protocolProfileBehavior} - Protocol profile behaviors
23
+ */
24
+ _protocolProfileBehavior: protocolProfileBehavior || {},
25
+
19
26
  /**
20
27
  * @param {String|Array<String>} keys
21
28
  * @return {*} Returns a value for a key or an object having all keys & values depending on the input
@@ -36,6 +36,7 @@ var ntlmUtil = require('httpntlm').ntlm,
36
36
  * - Down-Level Logon name format `DOMAIN\USERNAME`
37
37
  * - User Principal Name format `USERNAME@DOMAIN`
38
38
  *
39
+ * @private
39
40
  * @param {String} username - Username string to parse from
40
41
  * @return {Object} - An object with `username` and `domain` fields, which are `strings`.
41
42
  */
@@ -83,6 +84,26 @@ function parseParametersFromUsername (username) {
83
84
  };
84
85
  }
85
86
 
87
+ /**
88
+ * Check if `WWW-Authenticate` header has NTLM challenge.
89
+ *
90
+ * @private
91
+ * @param {*} headers - Postman headers instance
92
+ * @returns {Boolean}
93
+ */
94
+ function hasNTLMChallenge (headers) {
95
+ // Case 1: multiple headers
96
+ // - WWW-Authenticate: NTLM
97
+ // - WWW-Authenticate: Negotiate
98
+ if (headers.has(WWW_AUTHENTICATE, NTLM) || headers.has(WWW_AUTHENTICATE, NEGOTIATE)) {
99
+ return true;
100
+ }
101
+
102
+ // Case 2: single header
103
+ // - WWW-Authenticate: Negotiate, NTLM
104
+ return String(headers.get(WWW_AUTHENTICATE)).includes(NTLM);
105
+ }
106
+
86
107
  /**
87
108
  * NTLM auth while authenticating requires negotiateMessage (type 1) and authenticateMessage (type 3) to be stored.
88
109
  * Also it needs to know which stage is it in (INITIALIZED, T1_MSG_CREATED and T3_MSG_CREATED).
@@ -170,8 +191,7 @@ module.exports = {
170
191
 
171
192
  if (state === STATES.INITIALIZED) {
172
193
  // Nothing to do if the server does not ask us for auth in the first place.
173
- if (!(response.headers.has(WWW_AUTHENTICATE, NTLM) ||
174
- response.headers.has(WWW_AUTHENTICATE, NEGOTIATE))) {
194
+ if (!hasNTLMChallenge(response.headers)) {
175
195
  return done(null, true);
176
196
  }
177
197
 
@@ -42,7 +42,7 @@ function getOAuth1BaseUrl (url) {
42
42
  host = ((port === HTTP_PORT ||
43
43
  port === HTTPS_PORT ||
44
44
  port === undefined) && url.hostname) || url.host,
45
- path = url.pathname,
45
+ path = url.path,
46
46
 
47
47
  // trim to convert 'http:' from Node's URL object to 'http'
48
48
  protocol = _.trimEnd(url.protocol || PROTOCOL_HTTP, PROTOCOL_SEPARATOR);
@@ -52,6 +52,34 @@ function getOAuth1BaseUrl (url) {
52
52
  return protocol.toLowerCase() + host.toLowerCase() + path;
53
53
  }
54
54
 
55
+ /**
56
+ * Query parameters are encoded with WHATWG encoding in the request. OAuth1.0
57
+ * requires the query params to be encoded with the RFC-3986 standard. This
58
+ * function decodes the query parameters and encodes them to the required RFC-3986
59
+ * standard. For details: https://oauth.net/core/1.0a/#encoding_parameters
60
+ *
61
+ * @param {Request} request - request to update query parameters
62
+ * @param {Object} url - Node.js like url object
63
+ */
64
+ function updateQueryParamEncoding (request, url) {
65
+ // early bailout if no query is set.
66
+ if (!url.query) {
67
+ return;
68
+ }
69
+
70
+ const queryParams = oAuth1.decodeForm(url.query);
71
+
72
+ // clear all query parameters
73
+ request.url.query.clear();
74
+
75
+ _.forEach(queryParams, function (param) {
76
+ request.url.query.add({
77
+ key: param[0] && oAuth1.percentEncode(param[0]),
78
+ value: param[1] && oAuth1.percentEncode(param[1])
79
+ });
80
+ });
81
+ }
82
+
55
83
  /**
56
84
  * Calculates body hash with given algorithm and digestEncoding.
57
85
  *
@@ -252,13 +280,13 @@ module.exports = {
252
280
  *
253
281
  * @param {Request} request - request to add oauth1 parameters
254
282
  * @param {Object} params - oauth data to generate signature
283
+ * @param {Object} protocolProfileBehavior - Protocol profile behaviors
255
284
  * @param {Function} done - callback function
256
285
  */
257
- addAuthDataToRequest: function (request, params, done) {
286
+ addAuthDataToRequest: function (request, params, protocolProfileBehavior, done) {
258
287
  var url = urlEncoder.toNodeUrl(request.url),
259
288
  signatureParams,
260
289
  urlencodedBody,
261
- queryParams,
262
290
  bodyParams,
263
291
  allParams,
264
292
  signature,
@@ -268,7 +296,8 @@ module.exports = {
268
296
  consumerSecret: params.consumerSecret || EMPTY,
269
297
  tokenSecret: params.tokenSecret || EMPTY,
270
298
  privateKey: params.privateKey || EMPTY
271
- };
299
+ },
300
+ disableUrlEncoding = protocolProfileBehavior && protocolProfileBehavior.disableUrlEncoding;
272
301
 
273
302
  signatureParams = [
274
303
  {system: true, key: OAUTH1_PARAMS.oauthConsumerKey, value: params.consumerKey},
@@ -300,11 +329,6 @@ module.exports = {
300
329
  return params.addEmptyParamsToSign || param.value;
301
330
  });
302
331
 
303
- // filter disabled query parameters
304
- queryParams = request.url.query.filter(function (param) {
305
- return !param.disabled;
306
- });
307
-
308
332
  urlencodedBody = request.body &&
309
333
  request.body.mode === RequestBody.MODES.urlencoded &&
310
334
  request.body.urlencoded;
@@ -315,7 +339,7 @@ module.exports = {
315
339
  return !param.disabled;
316
340
  }) : [];
317
341
 
318
- allParams = [].concat(signatureParams, queryParams, bodyParams);
342
+ allParams = [].concat(signatureParams, bodyParams);
319
343
 
320
344
  message = {
321
345
  action: getOAuth1BaseUrl(url),
@@ -333,6 +357,13 @@ module.exports = {
333
357
  return done(err);
334
358
  }
335
359
 
360
+ // Update the encoding for query parameters to RFC-3986 in accordance with the
361
+ // OAuth1.0a specification: https://oauth.net/core/1.0a/#encoding_parameters
362
+ // disableUrlEncoding option should be respected in authorization flow as well
363
+ if (disableUrlEncoding !== true) {
364
+ updateQueryParamEncoding(request, url);
365
+ }
366
+
336
367
  signatureParams.push({system: true, key: OAUTH1_PARAMS.oauthSignature, value: signature});
337
368
 
338
369
  // Add signature params to the request. The OAuth specification says
@@ -358,9 +389,19 @@ module.exports = {
358
389
  urlencodedBody.add(param);
359
390
  });
360
391
  }
361
- else {
392
+ else if (disableUrlEncoding === true) {
393
+ // disableUrlEncoding option should be respected in authorization flow as well
362
394
  request.addQueryParams(signatureParams);
363
395
  }
396
+ else {
397
+ _.forEach(signatureParams, function (param) {
398
+ request.url.query.add({
399
+ key: param.key && oAuth1.percentEncode(param.key),
400
+ value: param.value && oAuth1.percentEncode(param.value),
401
+ system: true
402
+ });
403
+ });
404
+ }
364
405
 
365
406
  done();
366
407
  },
@@ -394,7 +435,8 @@ module.exports = {
394
435
  ]),
395
436
  urlencodedBody = request.body,
396
437
  signatureAlgo,
397
- hashAlgo;
438
+ hashAlgo,
439
+ protocolProfileBehavior = auth._protocolProfileBehavior;
398
440
 
399
441
  // extract hash and signature algorithm form signatureMethod
400
442
  // signature methods are in this format: '<signatureAlgo>-<hashAlgo>' e.g. RSA-SHA1
@@ -440,13 +482,13 @@ module.exports = {
440
482
  // Don't include body hash as defined in specification
441
483
  // @see: https://tools.ietf.org/id/draft-eaton-oauth-bodyhash-00.html#when_to_include
442
484
  if (urlencodedBody || !(params.includeBodyHash && hashAlgo)) {
443
- return self.addAuthDataToRequest(request, params, done);
485
+ return self.addAuthDataToRequest(request, params, protocolProfileBehavior, done);
444
486
  }
445
487
 
446
488
  computeBodyHash(request.body, hashAlgo, 'base64', function (bodyHash) {
447
489
  params.bodyHash = bodyHash;
448
490
 
449
- return self.addAuthDataToRequest(request, params, done);
491
+ return self.addAuthDataToRequest(request, params, protocolProfileBehavior, done);
450
492
  });
451
493
  }
452
494
  };
@@ -315,6 +315,9 @@ _.assign(Requester.prototype, /** @lends Requester.prototype */ {
315
315
  */
316
316
  onStart = function (response) {
317
317
  var responseStartEventName = RESPONSE_START_EVENT_BASE + id,
318
+ executionData,
319
+ initialRequest,
320
+ finalRequest,
318
321
  sdkResponse,
319
322
  history,
320
323
  done = function () {
@@ -342,12 +345,22 @@ _.assign(Requester.prototype, /** @lends Requester.prototype */ {
342
345
  // prepare history from request debug data
343
346
  history = getExecutionHistory(_.get(response, 'request._debug'));
344
347
 
348
+ // get the initial and final (on redirect) request from history
349
+ executionData = _.get(history, 'execution.data') || [];
350
+ initialRequest = _.get(executionData, '[0].request') || {};
351
+ finalRequest = executionData.length > 1 ?
352
+ // get final redirect
353
+ _.get(executionData, [executionData.length - 1, 'request']) :
354
+ // no redirects
355
+ initialRequest;
356
+
345
357
  // add missing request headers so that they get bubbled up into the UI
346
- addMissingRequestHeaders(_.get(history, 'execution.data[0].request.headers'));
358
+ addMissingRequestHeaders(initialRequest.headers);
347
359
 
348
- // Pull out cookies from the cookie jar, and make them chrome compatible.
360
+ // pull out cookies from the cookie jar, and make them chrome compatible.
349
361
  if (cookieJar && _.isFunction(cookieJar.getCookies)) {
350
- cookieJar.getCookies(requestOptions.url, function (err, cookiesFromJar) {
362
+ // get cookies set for the final request URL
363
+ cookieJar.getCookies(finalRequest.href, function (err, cookiesFromJar) {
351
364
  if (err) {
352
365
  return done();
353
366
  }
@@ -371,7 +384,7 @@ _.assign(Requester.prototype, /** @lends Requester.prototype */ {
371
384
  // we can't trust the integrity of this request
372
385
  // bail out if request url is empty
373
386
  if (!(request && request.url && request.url.toString && request.url.toString())) {
374
- return onEnd(new Error('runtime:extenstions~request: request url is empty'));
387
+ return onEnd(new Error('runtime:extensions~request: request url is empty'));
375
388
  }
376
389
 
377
390
  cookieJar = self.options.cookieJar;
@@ -127,6 +127,9 @@ pool = function (processors) {
127
127
  doneAndSpread = function (err) {
128
128
  if (sealed) {
129
129
  console.error('__postmanruntime_fatal_debug: instruction.execute callback called twice');
130
+ if (err) {
131
+ console.error(err);
132
+ }
130
133
 
131
134
  return;
132
135
  }
@@ -190,7 +190,7 @@ module.exports = [
190
190
  return done();
191
191
  }
192
192
 
193
- authInterface = createAuthInterface(auth);
193
+ authInterface = createAuthInterface(auth, context.protocolProfileBehavior);
194
194
 
195
195
  /**
196
196
  * We go through the `pre` request send validation for the auth. In this step one of the three things can happen
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postman-runtime",
3
- "version": "7.28.0",
3
+ "version": "7.28.4",
4
4
  "description": "Underlying library of executing Postman Collections (used by Newman)",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -8,6 +8,7 @@
8
8
  "test": "test"
9
9
  },
10
10
  "scripts": {
11
+ "codecov": "node npm/publish-coverage.js",
11
12
  "test": "npm run test-lint && npm run test-system && npm run test-unit && npm run test-integration && npm run test-integration-legacy",
12
13
  "test-system": "node npm/test-system.js",
13
14
  "test-lint": "node npm/test-lint.js",
@@ -39,23 +40,23 @@
39
40
  "eventemitter3": "4.0.7",
40
41
  "handlebars": "4.7.7",
41
42
  "http-reasons": "0.1.0",
42
- "httpntlm": "1.7.6",
43
+ "httpntlm": "1.7.7",
43
44
  "inherits": "2.0.4",
44
45
  "js-sha512": "0.8.0",
45
46
  "lodash": "4.17.21",
46
47
  "node-oauth1": "1.3.0",
47
48
  "performance-now": "2.1.0",
48
- "postman-collection": "3.6.11",
49
+ "postman-collection": "4.1.0",
49
50
  "postman-request": "2.88.1-postman.30",
50
- "postman-sandbox": "4.0.2",
51
- "postman-url-encoder": "3.0.1",
51
+ "postman-sandbox": "4.0.5",
52
+ "postman-url-encoder": "3.0.5",
52
53
  "resolve-from": "5.0.0",
53
54
  "serialised-error": "1.1.3",
54
55
  "tough-cookie": "3.0.1",
55
56
  "uuid": "3.4.0"
56
57
  },
57
58
  "devDependencies": {
58
- "@postman/shipit": "0.2.0",
59
+ "@postman/shipit": "0.3.0",
59
60
  "ajv": "6.12.6",
60
61
  "browserify": "16.5.2",
61
62
  "chai": "4.3.4",
@@ -63,13 +64,13 @@
63
64
  "editorconfig": "0.15.3",
64
65
  "eslint": "5.16.0",
65
66
  "eslint-plugin-jsdoc": "8.7.0",
66
- "eslint-plugin-lodash": "7.2.0",
67
+ "eslint-plugin-lodash": "7.3.0",
67
68
  "eslint-plugin-mocha": "6.3.0",
68
69
  "eslint-plugin-security": "1.4.0",
69
70
  "express": "4.17.1",
70
- "graphql": "15.5.0",
71
- "js-yaml": "4.0.0",
72
- "jsdoc": "3.6.6",
71
+ "graphql": "15.5.1",
72
+ "js-yaml": "4.1.0",
73
+ "jsdoc": "3.6.7",
73
74
  "jsdoc-to-markdown": "7.0.1",
74
75
  "karma": "3.1.4",
75
76
  "karma-browserify": "6.1.0",
@@ -87,7 +88,7 @@
87
88
  "shelljs": "0.8.4",
88
89
  "sinon": "8.1.1",
89
90
  "teleport-javascript": "1.0.0",
90
- "terser": "5.6.1",
91
+ "terser": "5.7.1",
91
92
  "tmp": "0.2.1",
92
93
  "yankee": "1.0.8"
93
94
  },