Skip to content

Commit c07579b

Browse files
HazATbenvinegar
authored andcommitted
Copy object to prevent exception in react native (#960)
* Copy object to prevent exception in react native * Add isFrozen check before merging objects * Fix tests * Add tests for frozen object * Add comment explaining function usage objectFrozen
1 parent cf99a21 commit c07579b

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

src/raven.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,16 +1385,17 @@ Raven.prototype = {
13851385

13861386
for (var i = 0; i < breadcrumbs.values.length; ++i) {
13871387
crumb = breadcrumbs.values[i];
1388-
if (!crumb.hasOwnProperty('data') || !isObject(crumb.data))
1388+
if (!crumb.hasOwnProperty('data') || !isObject(crumb.data) || objectFrozen(crumb.data))
13891389
continue;
13901390

1391-
data = crumb.data;
1391+
data = objectMerge({}, crumb.data);
13921392
for (var j = 0; j < urlProps.length; ++j) {
13931393
urlProp = urlProps[j];
13941394
if (data.hasOwnProperty(urlProp)) {
13951395
data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength);
13961396
}
13971397
}
1398+
breadcrumbs.values[i].data = data;
13981399
}
13991400
},
14001401

@@ -1779,6 +1780,21 @@ function objectMerge(obj1, obj2) {
17791780
return obj1;
17801781
}
17811782

1783+
/**
1784+
* This function is only used for react-native.
1785+
* react-native freezes object that have already been sent over the
1786+
* js bridge. We need this function in order to check if the object is frozen.
1787+
* So it's ok that objectFrozen returns false if Object.isFrozen is not
1788+
* supported because it's not relevant for other "platforms". See related issue:
1789+
* https://github.com/getsentry/react-native-sentry/issues/57
1790+
*/
1791+
function objectFrozen(obj) {
1792+
if (!Object.isFrozen) {
1793+
return false;
1794+
}
1795+
return Object.isFrozen(obj);
1796+
}
1797+
17821798
function truncate(str, max) {
17831799
return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
17841800
}

test/raven.test.js

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,90 @@ describe('globals', function() {
13141314
assert.equal(Raven._backoffStart, null); // clock is at 100ms
13151315
assert.equal(Raven._backoffDuration, 0);
13161316
});
1317+
it('should truncate url in breadcrumb', function() {
1318+
this.sinon.stub(Raven, 'isSetup').returns(true);
1319+
this.sinon.stub(Raven, '_makeRequest');
1320+
this.sinon.stub(Raven, '_getHttpData').returns({
1321+
url: 'http://localhost/?a=b',
1322+
headers: {'User-Agent': 'lolbrowser'}
1323+
});
1324+
1325+
Raven._globalProject = '2';
1326+
Raven._globalOptions = {
1327+
logger: 'javascript',
1328+
maxMessageLength: 100
1329+
};
1330+
Raven._globalOptions.maxUrlLength = 30;
1331+
1332+
var longUrl = new Array(50).join('a');
1333+
var obj = {method: 'POST', url: 'http://example.org/api/0/auth/' + longUrl};
1334+
Raven._breadcrumbs = [{type: 'request', timestamp: 0.1, data: obj}];
1335+
1336+
Raven._send({message: 'bar'});
1337+
assert.deepEqual(Raven._makeRequest.lastCall.args[0].data, {
1338+
project: '2',
1339+
logger: 'javascript',
1340+
platform: 'javascript',
1341+
request: {
1342+
url: 'http://localhost/?a=b',
1343+
headers: {
1344+
'User-Agent': 'lolbrowser'
1345+
}
1346+
},
1347+
event_id: 'abc123',
1348+
message: 'bar',
1349+
extra: {'session:duration': 100},
1350+
breadcrumbs: {
1351+
values: [
1352+
{ type: 'request', timestamp: 0.1, data: { method: 'POST', url: 'http://example.org/api/0/auth/…' }}
1353+
]
1354+
}
1355+
});
1356+
});
1357+
1358+
it('should skip truncating url in breadcrumb if object is frozen', function() {
1359+
this.sinon.stub(Raven, 'isSetup').returns(true);
1360+
this.sinon.stub(Raven, '_makeRequest');
1361+
this.sinon.stub(Raven, '_getHttpData').returns({
1362+
url: 'http://localhost/?a=b',
1363+
headers: {'User-Agent': 'lolbrowser'}
1364+
});
1365+
1366+
Raven._globalProject = '2';
1367+
Raven._globalOptions = {
1368+
logger: 'javascript',
1369+
maxMessageLength: 100
1370+
};
1371+
Raven._globalOptions.maxUrlLength = 35;
1372+
1373+
var longUrl = new Array(50).join('a');
1374+
var obj = {method: 'POST', url: 'http://example.org/api/0/auth/' + longUrl};
1375+
Object.freeze(obj);
1376+
1377+
Raven._breadcrumbs = [{type: 'request', timestamp: 0.1, data: obj}];
1378+
1379+
Raven._send({message: 'bar'});
1380+
assert.deepEqual(Raven._makeRequest.lastCall.args[0].data, {
1381+
project: '2',
1382+
logger: 'javascript',
1383+
platform: 'javascript',
1384+
request: {
1385+
url: 'http://localhost/?a=b',
1386+
headers: {
1387+
'User-Agent': 'lolbrowser'
1388+
}
1389+
},
1390+
event_id: 'abc123',
1391+
message: 'bar',
1392+
extra: {'session:duration': 100},
1393+
breadcrumbs: {
1394+
values: [
1395+
{ type: 'request', timestamp: 0.1, data: { method: 'POST', url: 'http://example.org/api/0/auth/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }}
1396+
]
1397+
}
1398+
});
1399+
});
1400+
13171401
});
13181402

13191403
describe('makeRequest', function() {
@@ -2770,7 +2854,7 @@ describe('Raven (private methods)', function () {
27702854
this.clock.tick(0); // Raven initialized at time "0"
27712855
Raven = new _Raven();
27722856
});
2773-
2857+
27742858
afterEach(function () {
27752859
this.clock.restore();
27762860
});

0 commit comments

Comments
 (0)