From 9b222a8ddf79b62773bce00a38665ccf21a0442a Mon Sep 17 00:00:00 2001 From: Dan Skinner Date: Tue, 30 Jan 2024 13:44:27 +0000 Subject: [PATCH] gracefully degrade an XMLHTTPRequest with undefined function context --- .../network-breadcrumbs.js | 10 +++++-- .../test/network-breadcrumbs.test.ts | 27 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/plugin-network-breadcrumbs/network-breadcrumbs.js b/packages/plugin-network-breadcrumbs/network-breadcrumbs.js index 63bff65eca..86997b9caa 100644 --- a/packages/plugin-network-breadcrumbs/network-breadcrumbs.js +++ b/packages/plugin-network-breadcrumbs/network-breadcrumbs.js @@ -28,7 +28,10 @@ module.exports = (_ignoredUrls = [], win = window) => { const originalOpen = win.XMLHttpRequest.prototype.open win.XMLHttpRequest.prototype.open = function open (method, url) { - trackedRequests.set(this, { method, url }) + // it's possible for `this` to be `undefined`, which is not a valid key for a WeakMap + if (this) { + trackedRequests.set(this, { method, url }) + } originalOpen.apply(this, arguments) } @@ -50,7 +53,10 @@ module.exports = (_ignoredUrls = [], win = window) => { this.addEventListener('load', load) this.addEventListener('error', error) - requestHandlers.set(this, { load, error }) + // it's possible for `this` to be `undefined`, which is not a valid key for a WeakMap + if (this) { + requestHandlers.set(this, { load, error }) + } } originalSend.apply(this, arguments) diff --git a/packages/plugin-network-breadcrumbs/test/network-breadcrumbs.test.ts b/packages/plugin-network-breadcrumbs/test/network-breadcrumbs.test.ts index dd6b0816f9..9cdda0fc87 100644 --- a/packages/plugin-network-breadcrumbs/test/network-breadcrumbs.test.ts +++ b/packages/plugin-network-breadcrumbs/test/network-breadcrumbs.test.ts @@ -18,21 +18,20 @@ class XMLHttpRequest { send (fail: boolean, status: number | null = null) { if (fail) { - this._listeners.error.map(fn => fn()) + this?._listeners.error.map(fn => fn()) } else { this.status = status - this._listeners.load.map(fn => fn()) + this?._listeners.load.map(fn => fn()) } } addEventListener (evt: 'load'| 'error', listener: () => void) { - this._listeners[evt].push(listener) + this?._listeners[evt].push(listener) } removeEventListener (evt: 'load'| 'error', listener: () => void) { - for (let i = this._listeners[evt].length - 1; i >= 0; i--) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - if (listener.name === this._listeners[evt][i].name) delete this._listeners[evt][i] + for (let i = this?._listeners?.[evt]?.length ?? 0 - 1; i >= 0; i--) { + if (listener.name === this?._listeners?.[evt]?.[i]?.name) delete this?._listeners[evt][i] } } } @@ -205,6 +204,22 @@ describe('plugin: network breadcrumbs', () => { })) }) + it('should gracefully degrade an XMLHTTPRequest with undefined function context', () => { + const window = { XMLHttpRequest, WeakMap } as unknown as Window & typeof globalThis + + p = plugin([], window) + const client = new Client({ apiKey: 'aaaa-aaaa-aaaa-aaaa', plugins: [p] }) + + const request = new window.XMLHttpRequest() as unknown as XMLHttpRequest + const open = request.open + const send = request.send + + open('GET', 'https://another-domain.xyz/') + send(true) + + expect(client._breadcrumbs.length).toBe(0) + }) + it('should not leave a breadcrumb for request to bugsnag notify endpoint', () => { const window = { XMLHttpRequest, WeakMap } as unknown as Window & typeof globalThis