Skip to content

Commit c7c6a5b

Browse files
committed
fix(utils): Fix infinite recursion in dropUndefinedKeys
1 parent ef9a5d9 commit c7c6a5b

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

packages/utils/src/object.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { WrappedFunction } from '@sentry/types';
44

55
import { htmlTreeAsString } from './browser';
66
import { isElement, isError, isEvent, isInstanceOf, isPlainObject, isPrimitive } from './is';
7+
import { memoBuilder, MemoFunc } from './memo';
78
import { truncate } from './string';
89

910
/**
@@ -207,18 +208,32 @@ export function extractExceptionKeysForMessage(exception: Record<string, unknown
207208
* Works recursively on objects and arrays.
208209
*/
209210
export function dropUndefinedKeys<T>(val: T): T {
211+
return _dropUndefinedKeys(val, memoBuilder());
212+
}
213+
214+
function _dropUndefinedKeys<T>(val: T, memo: MemoFunc): T {
215+
const [memoize] = memo;
216+
210217
if (isPlainObject(val)) {
218+
if (memoize(val)) {
219+
return val;
220+
}
211221
const rv: { [key: string]: any } = {};
212222
for (const key of Object.keys(val)) {
213223
if (typeof val[key] !== 'undefined') {
214-
rv[key] = dropUndefinedKeys(val[key]);
224+
rv[key] = _dropUndefinedKeys(val[key], memo);
215225
}
216226
}
217227
return rv as T;
218228
}
219229

220230
if (Array.isArray(val)) {
221-
return (val as any[]).map(dropUndefinedKeys) as any;
231+
if (memoize(val)) {
232+
return val;
233+
}
234+
return (val as any[]).map(item => {
235+
return _dropUndefinedKeys(item, memo);
236+
}) as any;
222237
}
223238

224239
return val;

packages/utils/test/object.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,47 @@ describe('dropUndefinedKeys()', () => {
199199
},
200200
});
201201
});
202+
203+
test('objects with circular reference', () => {
204+
const dog: any = {
205+
food: undefined,
206+
};
207+
208+
const human = {
209+
brain: undefined,
210+
pets: dog,
211+
};
212+
213+
const rat = {
214+
scares: human,
215+
weight: '4kg',
216+
};
217+
218+
dog.chases = rat;
219+
220+
expect(dropUndefinedKeys(human)).toStrictEqual({
221+
pets: {
222+
chases: rat,
223+
},
224+
});
225+
});
226+
227+
test('arrays with circular reference', () => {
228+
const egg: any[] = [];
229+
230+
const chicken = {
231+
food: undefined,
232+
weight: '1kg',
233+
lays: egg,
234+
};
235+
236+
egg[0] = chicken;
237+
238+
expect(dropUndefinedKeys(chicken)).toStrictEqual({
239+
lays: egg,
240+
weight: '1kg',
241+
});
242+
});
202243
});
203244

204245
describe('objectify()', () => {

0 commit comments

Comments
 (0)