diff --git a/src/raven.js b/src/raven.js index ea1c00fc328d..53026c1d8c26 100644 --- a/src/raven.js +++ b/src/raven.js @@ -1385,16 +1385,17 @@ Raven.prototype = { for (var i = 0; i < breadcrumbs.values.length; ++i) { crumb = breadcrumbs.values[i]; - if (!crumb.hasOwnProperty('data') || !isObject(crumb.data)) + if (!crumb.hasOwnProperty('data') || !isObject(crumb.data) || objectFrozen(crumb.data)) continue; - data = crumb.data; + data = objectMerge({}, crumb.data); for (var j = 0; j < urlProps.length; ++j) { urlProp = urlProps[j]; if (data.hasOwnProperty(urlProp)) { data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength); } } + breadcrumbs.values[i].data = data; } }, @@ -1779,6 +1780,21 @@ function objectMerge(obj1, obj2) { return obj1; } +/** + * This function is only used for react-native. + * react-native freezes object that have already been sent over the + * js bridge. We need this function in order to check if the object is frozen. + * So it's ok that objectFrozen returns false if Object.isFrozen is not + * supported because it's not relevant for other "platforms". See related issue: + * https://github.com/getsentry/react-native-sentry/issues/57 + */ +function objectFrozen(obj) { + if (!Object.isFrozen) { + return false; + } + return Object.isFrozen(obj); +} + function truncate(str, max) { return !max || str.length <= max ? str : str.substr(0, max) + '\u2026'; } diff --git a/test/raven.test.js b/test/raven.test.js index db61866228b9..a5efe8f99ddf 100644 --- a/test/raven.test.js +++ b/test/raven.test.js @@ -1314,6 +1314,90 @@ describe('globals', function() { assert.equal(Raven._backoffStart, null); // clock is at 100ms assert.equal(Raven._backoffDuration, 0); }); + it('should truncate url in breadcrumb', function() { + this.sinon.stub(Raven, 'isSetup').returns(true); + this.sinon.stub(Raven, '_makeRequest'); + this.sinon.stub(Raven, '_getHttpData').returns({ + url: 'http://localhost/?a=b', + headers: {'User-Agent': 'lolbrowser'} + }); + + Raven._globalProject = '2'; + Raven._globalOptions = { + logger: 'javascript', + maxMessageLength: 100 + }; + Raven._globalOptions.maxUrlLength = 30; + + var longUrl = new Array(50).join('a'); + var obj = {method: 'POST', url: 'http://example.org/api/0/auth/' + longUrl}; + Raven._breadcrumbs = [{type: 'request', timestamp: 0.1, data: obj}]; + + Raven._send({message: 'bar'}); + assert.deepEqual(Raven._makeRequest.lastCall.args[0].data, { + project: '2', + logger: 'javascript', + platform: 'javascript', + request: { + url: 'http://localhost/?a=b', + headers: { + 'User-Agent': 'lolbrowser' + } + }, + event_id: 'abc123', + message: 'bar', + extra: {'session:duration': 100}, + breadcrumbs: { + values: [ + { type: 'request', timestamp: 0.1, data: { method: 'POST', url: 'http://example.org/api/0/auth/…' }} + ] + } + }); + }); + + it('should skip truncating url in breadcrumb if object is frozen', function() { + this.sinon.stub(Raven, 'isSetup').returns(true); + this.sinon.stub(Raven, '_makeRequest'); + this.sinon.stub(Raven, '_getHttpData').returns({ + url: 'http://localhost/?a=b', + headers: {'User-Agent': 'lolbrowser'} + }); + + Raven._globalProject = '2'; + Raven._globalOptions = { + logger: 'javascript', + maxMessageLength: 100 + }; + Raven._globalOptions.maxUrlLength = 35; + + var longUrl = new Array(50).join('a'); + var obj = {method: 'POST', url: 'http://example.org/api/0/auth/' + longUrl}; + Object.freeze(obj); + + Raven._breadcrumbs = [{type: 'request', timestamp: 0.1, data: obj}]; + + Raven._send({message: 'bar'}); + assert.deepEqual(Raven._makeRequest.lastCall.args[0].data, { + project: '2', + logger: 'javascript', + platform: 'javascript', + request: { + url: 'http://localhost/?a=b', + headers: { + 'User-Agent': 'lolbrowser' + } + }, + event_id: 'abc123', + message: 'bar', + extra: {'session:duration': 100}, + breadcrumbs: { + values: [ + { type: 'request', timestamp: 0.1, data: { method: 'POST', url: 'http://example.org/api/0/auth/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }} + ] + } + }); + }); + }); describe('makeRequest', function() { @@ -2770,7 +2854,7 @@ describe('Raven (private methods)', function () { this.clock.tick(0); // Raven initialized at time "0" Raven = new _Raven(); }); - + afterEach(function () { this.clock.restore(); });