Skip to content

Commit 1e653c9

Browse files
authored
fix(setupWorker): prevent WorkerChannel access in fallback mode (#2594)
1 parent 44d13d2 commit 1e653c9

File tree

5 files changed

+33
-13
lines changed

5 files changed

+33
-13
lines changed

src/browser/setupWorker/glossary.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ export type SetupWorkerInternalContext = {
2525
emitter: Emitter<LifeCycleEventsMap>
2626
keepAliveInterval?: number
2727
workerChannel: WorkerChannel
28-
supports: {
29-
serviceWorkerApi: boolean
30-
readableStreamTransfer: boolean
31-
}
3228
fallbackInterceptor?: Interceptor<HttpRequestEventMap>
3329
}
3430

src/browser/setupWorker/setupWorker.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { SetupApi } from '~/core/SetupApi'
1414
import { mergeRight } from '~/core/utils/internal/mergeRight'
1515
import type { LifeCycleEventsMap } from '~/core/sharedOptions'
1616
import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler'
17-
import { supportsReadableStreamTransfer } from '../utils/supportsReadableStreamTransfer'
1817
import { webSocketInterceptor } from '~/core/ws/webSocketInterceptor'
1918
import { handleWebSocketEvent } from '~/core/ws/handleWebSocketEvent'
2019
import { attachWebSocketLogger } from '~/core/ws/utils/attachWebSocketLogger'
@@ -23,6 +22,7 @@ import { DeferredPromise } from '@open-draft/deferred-promise'
2322
import { createFallbackRequestListener } from './start/createFallbackRequestListener'
2423
import { printStartMessage } from './start/utils/printStartMessage'
2524
import { printStopMessage } from './stop/utils/printStopMessage'
25+
import { supportsServiceWorker } from '../utils/supports'
2626

2727
export class SetupWorkerApi
2828
extends SetupApi<LifeCycleEventsMap>
@@ -60,11 +60,6 @@ export class SetupWorkerApi
6060
workerChannel: new WorkerChannel({
6161
worker: workerPromise,
6262
}),
63-
supports: {
64-
serviceWorkerApi:
65-
'serviceWorker' in navigator && location.protocol !== 'file:',
66-
readableStreamTransfer: supportsReadableStreamTransfer(),
67-
},
6863
}
6964
}
7065

@@ -117,7 +112,7 @@ export class SetupWorkerApi
117112

118113
// Use a fallback interception algorithm in the environments
119114
// where the Service Worker API isn't supported.
120-
if (!this.context.supports.serviceWorkerApi) {
115+
if (!supportsServiceWorker()) {
121116
const fallbackInterceptor = createFallbackRequestListener(
122117
this.context,
123118
this.context.startOptions,
@@ -159,7 +154,7 @@ export class SetupWorkerApi
159154
this.context.workerStoppedAt = Date.now()
160155
this.context.emitter.removeAllListeners()
161156

162-
if (this.context.supports.serviceWorkerApi) {
157+
if (supportsServiceWorker()) {
163158
this.context.workerChannel.removeAllListeners('RESPONSE')
164159
window.clearInterval(this.context.keepAliveInterval)
165160
}

src/browser/setupWorker/start/createRequestListener.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import { Emitter } from 'rettime'
22
import { StartOptions, SetupWorkerInternalContext } from '../glossary'
33
import { deserializeRequest } from '../../utils/deserializeRequest'
4+
import { supportsReadableStreamTransfer } from '../../utils/supports'
45
import { RequestHandler } from '~/core/handlers/RequestHandler'
56
import { handleRequest } from '~/core/utils/handleRequest'
67
import { RequiredDeep } from '~/core/typeUtils'
78
import { devUtils } from '~/core/utils/internal/devUtils'
89
import { toResponseInit } from '~/core/utils/toResponseInit'
910
import { isHandlerKind } from '~/core/utils/internal/isHandlerKind'
1011

12+
const SUPPORTS_READABLE_STREAM_TRANSFER = supportsReadableStreamTransfer()
13+
1114
export const createRequestListener = (
1215
context: SetupWorkerInternalContext,
1316
options: RequiredDeep<StartOptions>,
@@ -58,7 +61,7 @@ export const createRequestListener = (
5861
* @note Safari doesn't support transferring a "ReadableStream".
5962
* Check that the browser supports that before sending it to the worker.
6063
*/
61-
if (context.supports.readableStreamTransfer) {
64+
if (SUPPORTS_READABLE_STREAM_TRANSFER) {
6265
const responseStreamOrNull = response.body
6366

6467
event.postMessage(

src/browser/utils/supportsReadableStreamTransfer.ts renamed to src/browser/utils/supports.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
/**
2+
* Checks if the Service Worker API is supproted and available
3+
* in the current browsing context.
4+
*/
5+
export function supportsServiceWorker(): boolean {
6+
return (
7+
typeof navigator !== 'undefined' &&
8+
'serviceWorker' in navigator &&
9+
typeof location !== 'undefined' &&
10+
location.protocol !== 'file:'
11+
)
12+
}
13+
114
/**
215
* Returns a boolean indicating whether the current browser
316
* supports `ReadableStream` as a `Transferable` when posting

src/browser/utils/workerChannel.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { invariant } from 'outvariant'
12
import { Emitter, TypedEvent } from 'rettime'
23
import { isObject } from '~/core/utils/internal/isObject'
34
import type { StringifiedResponse } from '../setupWorker/glossary'
5+
import { supportsServiceWorker } from '../utils/supports'
46

57
export interface WorkerChannelOptions {
68
worker: Promise<ServiceWorker>
@@ -65,6 +67,8 @@ export type WorkerEventResponse = {
6567
PASSTHROUGH: []
6668
}
6769

70+
const SUPPORTS_SERVICE_WORKER = supportsServiceWorker()
71+
6872
export class WorkerEvent<
6973
DataType,
7074
ReturnType = any,
@@ -121,6 +125,10 @@ export class WorkerChannel extends Emitter<WorkerChannelEventMap> {
121125
constructor(protected readonly options: WorkerChannelOptions) {
122126
super()
123127

128+
if (!SUPPORTS_SERVICE_WORKER) {
129+
return
130+
}
131+
124132
navigator.serviceWorker.addEventListener('message', async (event) => {
125133
const worker = await this.options.worker
126134

@@ -139,6 +147,11 @@ export class WorkerChannel extends Emitter<WorkerChannelEventMap> {
139147
* This triggers the `message` event listener on ServiceWorkerGlobalScope.
140148
*/
141149
public postMessage(type: OutgoingWorkerEvents): void {
150+
invariant(
151+
SUPPORTS_SERVICE_WORKER,
152+
'Failed to post message on a WorkerChannel: the Service Worker API is unavailable in this context. This is likely an issue with MSW. Please report it on GitHub: https://github.com/mswjs/msw/issues',
153+
)
154+
142155
this.options.worker.then((worker) => {
143156
worker.postMessage(type)
144157
})

0 commit comments

Comments
 (0)