Skip to content

Commit 43418f8

Browse files
committed
fix: avoid browser crashes when serializing browser logs of complex objects
If we are seeing too many object iterations, we know something is off. This can happen when we are seeing e.g. large linked lists where every element recursively has access to the full list again. This will not quickly yield circular early bail-outs, but instead result in the stringification to take LOTS of processing time and results in browser crashes. We should limit object iterations for this reason, and instead expect people to inspect such logs in the browser directly. Related #2798.
1 parent 29f73c5 commit 43418f8

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

.changeset/three-gifts-clap.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@web/browser-logs': patch
3+
---
4+
5+
Avoid browser crashes when serializing browser logs of complex objects
6+
7+
If we are seeing too many object iterations, we know something is off. This can
8+
happen when we are seeing e.g. large linked lists where every element recursively
9+
has access to the full list again. This will not quickly yield circular early bail-outs,
10+
but instead result in the stringification to take LOTS of processing time and results
11+
in browser crashes. We should limit object iterations for this reason, and instead expect
12+
people to inspect such logs in the browser directly.
13+
14+
Related #2798.

packages/browser-logs/src/serialize.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ function serializeObject(value: any) {
7171
function createReplacer() {
7272
// maintain a stack of seen objects to handle circular references
7373
var objectStack: any[] = [];
74+
var objectIterations = 0;
7475

7576
return function replacer(this: any, key: string, value: unknown) {
7677
if (this[KEY_WTR_TYPE]) {
@@ -113,6 +114,17 @@ function createReplacer() {
113114
}
114115
objectStack.unshift(value);
115116

117+
// If we are seeing too many object iterations, we know something is off. This can
118+
// happen when we are seeing e.g. large linked lists where every element recursively
119+
// has access to the full list again. This will not quickly yield circular early bail-outs,
120+
// but instead result in the stringification to take LOTS of processing time and results
121+
// in browser crashes. We should limit object iterations for this reason, and instead expect
122+
// people to inspect such logs in the browser directly.
123+
objectIterations++;
124+
if (objectIterations > 10_000) {
125+
return '[Serialization Limit]';
126+
}
127+
116128
if (Array.isArray(value)) {
117129
return value;
118130
}

packages/browser-logs/test/serialize-deserialize.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,46 @@ describe('serialize deserialize', function () {
336336
expect(deserialized.x).to.equal('[Circular]');
337337
});
338338

339+
it('handles complex circular references', async () => {
340+
const serialized = await page.evaluate(() => {
341+
interface ChipNode {
342+
idx: number;
343+
next?: ChipNode;
344+
prev?: ChipNode;
345+
val: unknown;
346+
}
347+
class MatChip {
348+
viewRef: { nodes: ChipNode[] } = { nodes: [] };
349+
350+
constructor() {
351+
let prevNode: ChipNode | null = null;
352+
for (let i = 0; i < 1000; i++) {
353+
const newNode: ChipNode = {
354+
idx: i,
355+
val: this,
356+
next: this.viewRef.nodes[0],
357+
};
358+
359+
this.viewRef.nodes.push(newNode);
360+
361+
if (prevNode) {
362+
prevNode.next = newNode;
363+
newNode.prev = prevNode;
364+
}
365+
366+
prevNode = newNode;
367+
}
368+
}
369+
}
370+
return (window as any)._serialize(new MatChip());
371+
});
372+
373+
await deserialize(serialized);
374+
375+
// No assertion. Simply expect this to pass and not crash.
376+
expect(true).to.eq(true);
377+
});
378+
339379
it('handles errors', async () => {
340380
const serialized = await page.evaluate(() => {
341381
const c = () => new Error('my error msg');

0 commit comments

Comments
 (0)