rollbar 2.25.1 → 2.25.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rollbar",
3
- "version": "2.25.1",
3
+ "version": "2.25.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "http://github.com/rollbar/rollbar.js"
@@ -1,4 +1,5 @@
1
1
  var _ = require('../utility');
2
+ var headers = require('../utility/headers');
2
3
  var scrub = require('../scrub');
3
4
  var urlparser = require('./url');
4
5
  var domUtil = require('./domUtility');
@@ -362,7 +363,7 @@ Instrumenter.prototype.instrumentNetwork = function() {
362
363
  if (args[1] && args[1].headers) {
363
364
  // Argument may be a Headers object, or plain object. Ensure here that
364
365
  // we are working with a Headers object with case-insensitive keys.
365
- var reqHeaders = new Headers(args[1].headers);
366
+ var reqHeaders = headers(args[1].headers);
366
367
 
367
368
  metadata.request_content_type = reqHeaders.get('Content-Type');
368
369
 
@@ -79,20 +79,25 @@ function addBaseInfo(item, options, callback) {
79
79
 
80
80
  function addRequestInfo(window) {
81
81
  return function(item, options, callback) {
82
- if (!window || !window.location) {
83
- return callback(null, item);
82
+ var requestInfo = {};
83
+
84
+ if (window && window.location) {
85
+ requestInfo.url = window.location.href;
86
+ requestInfo.query_string = window.location.search;
84
87
  }
88
+
85
89
  var remoteString = '$remote_ip';
86
90
  if (!options.captureIp) {
87
91
  remoteString = null;
88
92
  } else if (options.captureIp !== true) {
89
93
  remoteString += '_anonymize';
90
94
  }
91
- _.set(item, 'data.request', {
92
- url: window.location.href,
93
- query_string: window.location.search,
94
- user_ip: remoteString
95
- });
95
+ if (remoteString) requestInfo.user_ip = remoteString;
96
+
97
+ if (Object.keys(requestInfo).length > 0) {
98
+ _.set(item, 'data.request', requestInfo);
99
+ }
100
+
96
101
  callback(null, item);
97
102
  };
98
103
  }
package/src/defaults.js CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- version: '2.25.1',
2
+ version: '2.25.2',
3
3
  endpoint: 'api.rollbar.com/api/1/item/',
4
4
  logLevel: 'debug',
5
5
  reportLevel: 'debug',
@@ -0,0 +1,94 @@
1
+ /*
2
+ * headers - Detect when fetch Headers are undefined and use a partial polyfill.
3
+ *
4
+ * A full polyfill is not used in order to keep package size as small as possible.
5
+ * Since this is only used internally and is not added to the window object,
6
+ * the full interface doesn't need to be supported.
7
+ *
8
+ * This implementation is modified from whatwg-fetch:
9
+ * https://github.com/github/fetch
10
+ */
11
+ function headers(headers) {
12
+ if (typeof Headers === 'undefined') {
13
+ return new FetchHeaders(headers);
14
+ }
15
+
16
+ return new Headers(headers);
17
+ }
18
+
19
+ function normalizeName(name) {
20
+ if (typeof name !== 'string') {
21
+ name = String(name)
22
+ }
23
+ return name.toLowerCase()
24
+ }
25
+
26
+ function normalizeValue(value) {
27
+ if (typeof value !== 'string') {
28
+ value = String(value)
29
+ }
30
+ return value
31
+ }
32
+
33
+ function iteratorFor(items) {
34
+ var iterator = {
35
+ next: function() {
36
+ var value = items.shift()
37
+ return {done: value === undefined, value: value}
38
+ }
39
+ }
40
+
41
+ return iterator
42
+ }
43
+
44
+ function FetchHeaders(headers) {
45
+ this.map = {}
46
+
47
+ if (headers instanceof FetchHeaders) {
48
+ headers.forEach(function(value, name) {
49
+ this.append(name, value)
50
+ }, this)
51
+ } else if (Array.isArray(headers)) {
52
+ headers.forEach(function(header) {
53
+ this.append(header[0], header[1])
54
+ }, this)
55
+ } else if (headers) {
56
+ Object.getOwnPropertyNames(headers).forEach(function(name) {
57
+ this.append(name, headers[name])
58
+ }, this)
59
+ }
60
+ }
61
+
62
+ FetchHeaders.prototype.append = function(name, value) {
63
+ name = normalizeName(name)
64
+ value = normalizeValue(value)
65
+ var oldValue = this.map[name]
66
+ this.map[name] = oldValue ? oldValue + ', ' + value : value
67
+ }
68
+
69
+ FetchHeaders.prototype.get = function(name) {
70
+ name = normalizeName(name)
71
+ return this.has(name) ? this.map[name] : null
72
+ }
73
+
74
+ FetchHeaders.prototype.has = function(name) {
75
+ return this.map.hasOwnProperty(normalizeName(name))
76
+ }
77
+
78
+ FetchHeaders.prototype.forEach = function(callback, thisArg) {
79
+ for (var name in this.map) {
80
+ if (this.map.hasOwnProperty(name)) {
81
+ callback.call(thisArg, this.map[name], name, this)
82
+ }
83
+ }
84
+ }
85
+
86
+ FetchHeaders.prototype.entries = function() {
87
+ var items = []
88
+ this.forEach(function(value, name) {
89
+ items.push([name, value])
90
+ })
91
+ return iteratorFor(items)
92
+ }
93
+
94
+ module.exports = headers;
@@ -1495,17 +1495,21 @@ describe('options.autoInstrument', function() {
1495
1495
  if(xhr.readyState === 4) {
1496
1496
  try {
1497
1497
  setTimeout(function() {
1498
- server.respond();
1498
+ try {
1499
+ server.respond();
1499
1500
 
1500
- expect(server.requests.length).to.eql(2);
1501
- var body = JSON.parse(server.requests[1].requestBody);
1501
+ expect(server.requests.length).to.eql(2);
1502
+ var body = JSON.parse(server.requests[1].requestBody);
1502
1503
 
1503
- expect(body.data.body.trace.exception.message).to.eql('HTTP request failed with Status 404');
1504
+ expect(body.data.body.trace.exception.message).to.eql('HTTP request failed with Status 404');
1504
1505
 
1505
- // Just knowing a stack is present is enough for this test.
1506
- expect(body.data.body.trace.frames.length).to.be.above(1);
1506
+ // Just knowing a stack is present is enough for this test.
1507
+ expect(body.data.body.trace.frames.length).to.be.above(1);
1507
1508
 
1508
- done();
1509
+ done();
1510
+ } catch (e) {
1511
+ done(e);
1512
+ }
1509
1513
  }, 1);
1510
1514
  } catch (e) {
1511
1515
  done(e);
@@ -1606,7 +1610,7 @@ describe('options.autoInstrument', function() {
1606
1610
  })
1607
1611
  });
1608
1612
 
1609
- it('should add telemetry events for fetch calls', function(done) {
1613
+ it('should report error for http 4xx fetch calls, when enabled', function(done) {
1610
1614
  var server = window.server;
1611
1615
  stubResponse(server);
1612
1616
  server.requests.length = 0;
@@ -1659,6 +1663,80 @@ describe('options.autoInstrument', function() {
1659
1663
  })
1660
1664
  });
1661
1665
 
1666
+ it('should add telemetry headers when fetch Headers object is undefined', function(done) {
1667
+ var server = window.server;
1668
+ stubResponse(server);
1669
+ server.requests.length = 0;
1670
+
1671
+ window.fetchStub = sinon.stub(window, 'fetch');
1672
+
1673
+ var readableStream = new ReadableStream({
1674
+ start(controller) {
1675
+ controller.enqueue(JSON.stringify({name: 'foo', password: '123456'}));
1676
+ controller.close();
1677
+ }
1678
+ });
1679
+
1680
+ window.fetch.returns(Promise.resolve(new Response(
1681
+ readableStream,
1682
+ { status: 200, statusText: 'OK', headers: { 'content-type': 'application/json', 'password': '123456' }}
1683
+ )));
1684
+
1685
+ var options = {
1686
+ accessToken: 'POST_CLIENT_ITEM_TOKEN',
1687
+ autoInstrument: {
1688
+ log: false,
1689
+ network: true,
1690
+ networkResponseHeaders: true,
1691
+ networkRequestHeaders: true
1692
+ }
1693
+ };
1694
+ var rollbar = window.rollbar = new Rollbar(options);
1695
+
1696
+ // Remove Headers from window object
1697
+ var originalHeaders = window.Headers;
1698
+ delete window.Headers;
1699
+
1700
+ const fetchInit = {
1701
+ method: 'POST',
1702
+ headers: {'Content-Type': 'application/json', Secret: '123456'},
1703
+ body: JSON.stringify({name: 'bar', secret: 'xhr post'})
1704
+ };
1705
+ var fetchRequest = new Request('https://example.com/xhr-test');
1706
+ window.fetch(fetchRequest, fetchInit)
1707
+ .then(function(response) {
1708
+ try {
1709
+ rollbar.log('test'); // generate a payload to inspect
1710
+ setTimeout(function() {
1711
+ try {
1712
+ server.respond();
1713
+
1714
+ expect(server.requests.length).to.eql(1);
1715
+ var body = JSON.parse(server.requests[0].requestBody);
1716
+
1717
+ // Verify request headers capture and case-insensitive scrubbing
1718
+ expect(body.data.body.telemetry[0].body.request_headers).to.eql({'content-type': 'application/json', secret: '********'});
1719
+
1720
+ // Verify response headers capture and case-insensitive scrubbing
1721
+ expect(body.data.body.telemetry[0].body.response.headers).to.eql({'content-type': 'application/json', password: '********'});
1722
+
1723
+ // Assert that the original stream reader hasn't been read.
1724
+ expect(response.bodyUsed).to.eql(false);
1725
+
1726
+ rollbar.configure({ autoInstrument: false });
1727
+ window.fetch.restore();
1728
+ window.Headers = originalHeaders;
1729
+ done();
1730
+ } catch (e) {
1731
+ done(e);
1732
+ }
1733
+ });
1734
+ } catch (e) {
1735
+ done(e);
1736
+ }
1737
+ })
1738
+ });
1739
+
1662
1740
  it('should add a diagnostic message when wrapConsole fails', function(done) {
1663
1741
  var server = window.server;
1664
1742
  stubResponse(server);
@@ -226,9 +226,10 @@ describe('addRequestInfo', function() {
226
226
  it('should use window info to set request properties', function(done) {
227
227
  var args = ['a message'];
228
228
  var item = itemFromArgs(args);
229
- var options = {};
229
+ var options = { captureIp: 'anonymize' };
230
230
  t.addRequestInfo(window)(item, options, function(e, i) {
231
231
  expect(i.data.request).to.be.ok();
232
+ expect(i.data.request.user_ip).to.eql('$remote_ip_anonymize');
232
233
  done(e);
233
234
  });
234
235
  });
@@ -243,6 +244,19 @@ describe('addRequestInfo', function() {
243
244
  done(e);
244
245
  });
245
246
  });
247
+ it('should honor captureIp without window', function(done) {
248
+ var args = ['a message'];
249
+ var item = itemFromArgs(args);
250
+ item.data = {};
251
+ var options = { captureIp: true };
252
+ var w = null;
253
+ t.addRequestInfo(w)(item, options, function(e, i) {
254
+ expect(i.data.request.url).to.not.be.ok();
255
+ expect(i.data.request.query_string).to.not.be.ok();
256
+ expect(i.data.request.user_ip).to.eql('$remote_ip');
257
+ done(e);
258
+ });
259
+ });
246
260
  });
247
261
 
248
262
  describe('addClientInfo', function() {