Skip to content

Commit 98a46ba

Browse files
authored
fix: CORS errors should be TypeErrors (#70)
1 parent 0e529ee commit 98a46ba

File tree

4 files changed

+161
-126
lines changed

4 files changed

+161
-126
lines changed

src/cors.js

Lines changed: 53 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -264,48 +264,31 @@ function isValidOrigin(origin) {
264264
//-----------------------------------------------------------------------------
265265

266266
/**
267-
* Represents an error that occurs when a CORS request is blocked.
267+
* Creates a CORS error object.
268+
* @param {string} requestUrl The URL of the request.
269+
* @param {string} origin The origin of the client making the request.
270+
* @param {string} message The error message.
271+
* @returns {TypeError} A TypeError with CORS error message.
268272
*/
269-
export class CorsError extends Error {
270-
/**
271-
* The name of the error.
272-
* @type {string}
273-
*/
274-
name = "CorsError";
275-
276-
/**
277-
* Creates a new instance.
278-
* @param {string} requestUrl The URL of the request.
279-
* @param {string} origin The origin of the client making the request.
280-
* @param {string} message The error message.
281-
*/
282-
constructor(requestUrl, origin, message) {
283-
super(
284-
`Access to fetch at '${requestUrl}' from origin '${origin}' has been blocked by CORS policy: ${message}`,
285-
);
286-
}
273+
export function createCorsError(requestUrl, origin, message) {
274+
return new TypeError(
275+
`Access to fetch at '${requestUrl}' from origin '${origin}' has been blocked by CORS policy: ${message}`
276+
);
287277
}
288278

289279
/**
290-
* Represents an error that occurs when a CORS preflight request fails.
280+
* Creates a CORS preflight error object.
281+
* @param {string} requestUrl The URL of the request.
282+
* @param {string} origin The origin of the client making the request.
283+
* @param {string} message The error message.
284+
* @returns {TypeError} A TypeError with CORS preflight error message.
291285
*/
292-
export class CorsPreflightError extends CorsError {
293-
294-
/**
295-
* The name of the error.
296-
* @type {string}
297-
*/
298-
name = "CorsPreflightError";
299-
300-
/**
301-
* Creates a new instance.
302-
* @param {string} requestUrl The URL of the request.
303-
* @param {string} origin The origin of the client making the request.
304-
* @param {string} message The error message.
305-
*/
306-
constructor(requestUrl, origin, message) {
307-
super(requestUrl, origin, `Response to preflight request doesn't pass access control check: ${message}`);
308-
}
286+
export function createCorsPreflightError(requestUrl, origin, message) {
287+
return createCorsError(
288+
requestUrl,
289+
origin,
290+
`Response to preflight request doesn't pass access control check: ${message}`
291+
);
309292
}
310293

311294
/**
@@ -318,42 +301,42 @@ export class CorsPreflightError extends CorsError {
318301
*/
319302
export function assertCorsResponse(response, origin, isPreflight = false) {
320303
const originHeader = response.headers.get(CORS_ALLOW_ORIGIN);
321-
const NetworkError = isPreflight ? CorsPreflightError : CorsError;
304+
const errorCreator = isPreflight ? createCorsPreflightError : createCorsError;
322305

323306
if (!originHeader) {
324-
throw new NetworkError(
307+
throw errorCreator(
325308
response.url,
326309
origin,
327-
"No 'Access-Control-Allow-Origin' header is present on the requested resource.",
310+
"No 'Access-Control-Allow-Origin' header is present on the requested resource."
328311
);
329312
}
330-
313+
331314
// multiple values are not allowed
332315
if (originHeader.includes(",")) {
333-
throw new NetworkError(
316+
throw errorCreator(
334317
response.url,
335318
origin,
336-
`The 'Access-Control-Allow-Origin' header contains multiple values '${originHeader}', but only one is allowed.`,
319+
`The 'Access-Control-Allow-Origin' header contains multiple values '${originHeader}', but only one is allowed.`
337320
);
338321
}
339-
322+
340323
if (originHeader !== "*") {
341324
// must be a valid origin
342325
if (!isValidOrigin(origin)) {
343-
throw new NetworkError(
326+
throw errorCreator(
344327
response.url,
345328
origin,
346-
`The 'Access-Control-Allow-Origin' header contains the invalid value '${originHeader}'.`,
329+
`The 'Access-Control-Allow-Origin' header contains the invalid value '${originHeader}'.`
347330
);
348331
}
349332

350333
const originUrl = new URL(origin);
351334

352335
if (originUrl.origin !== originHeader) {
353-
throw new NetworkError(
336+
throw errorCreator(
354337
response.url,
355338
origin,
356-
`The 'Access-Control-Allow-Origin' header has a value '${originHeader}' that is not equal to the supplied origin.`,
339+
`The 'Access-Control-Allow-Origin' header has a value '${originHeader}' that is not equal to the supplied origin.`
357340
);
358341
}
359342
}
@@ -371,50 +354,50 @@ export function assertCorsCredentials(response, origin) {
371354
const allowCredentials = response.headers.get(CORS_ALLOW_CREDENTIALS);
372355

373356
if (!allowCredentials) {
374-
throw new CorsError(
357+
throw createCorsError(
375358
response.url,
376359
origin,
377-
"No 'Access-Control-Allow-Credentials' header is present on the requested resource.",
360+
"No 'Access-Control-Allow-Credentials' header is present on the requested resource."
378361
);
379362
}
380363

381364
if (allowCredentials !== "true") {
382-
throw new CorsError(
365+
throw createCorsError(
383366
response.url,
384367
origin,
385-
"The 'Access-Control-Allow-Credentials' header has a value that is not 'true'.",
368+
"The 'Access-Control-Allow-Credentials' header has a value that is not 'true'."
386369
);
387370
}
388371

389372
if (response.headers.get(CORS_ALLOW_ORIGIN) === "*") {
390-
throw new CorsError(
373+
throw createCorsError(
391374
response.url,
392375
origin,
393-
"The 'Access-Control-Allow-Origin' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'.",
376+
"The 'Access-Control-Allow-Origin' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'."
394377
);
395378
}
396379

397380
if (response.headers.get(CORS_ALLOW_HEADERS) === "*") {
398-
throw new CorsError(
381+
throw createCorsError(
399382
response.url,
400383
origin,
401-
"The 'Access-Control-Allow-Headers' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'.",
384+
"The 'Access-Control-Allow-Headers' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'."
402385
);
403386
}
404387

405388
if (response.headers.get(CORS_ALLOW_METHODS) === "*") {
406-
throw new CorsError(
389+
throw createCorsError(
407390
response.url,
408391
origin,
409-
"The 'Access-Control-Allow-Methods' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'.",
392+
"The 'Access-Control-Allow-Methods' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'."
410393
);
411394
}
412395

413396
if (response.headers.get(CORS_EXPOSE_HEADERS) === "*") {
414-
throw new CorsError(
397+
throw createCorsError(
415398
response.url,
416399
origin,
417-
"The 'Access-Control-Expose-Headers' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'.",
400+
"The 'Access-Control-Expose-Headers' header has a value of '*' which is not allowed when the 'Access-Control-Allow-Credentials' header is 'true'."
418401
);
419402
}
420403
}
@@ -545,10 +528,10 @@ export function isCorsSimpleRequest(request) {
545528
export function validateCorsRequest(request, origin) {
546529
// check the method
547530
if (forbiddenMethods.has(request.method)) {
548-
throw new CorsError(
531+
throw createCorsError(
549532
request.url,
550533
origin,
551-
`Method ${request.method} is not allowed.`,
534+
`Method ${request.method} is not allowed.`
552535
);
553536
}
554537

@@ -558,10 +541,10 @@ export function validateCorsRequest(request, origin) {
558541
const value = /** @type {string} */ (request.headers.get(header));
559542

560543
if (isForbiddenRequestHeader(header, value)) {
561-
throw new CorsError(
544+
throw createCorsError(
562545
request.url,
563546
origin,
564-
`Header ${header} is not allowed.`,
547+
`Header ${header} is not allowed.`
565548
);
566549
}
567550
}
@@ -688,10 +671,10 @@ export class CorsPreflightData {
688671
!safeMethods.has(method) &&
689672
!this.allowedMethods.has(method)
690673
) {
691-
throw new CorsError(
674+
throw createCorsError(
692675
request.url,
693676
origin,
694-
`Method ${method} is not allowed.`,
677+
`Method ${method} is not allowed.`
695678
);
696679
}
697680
}
@@ -719,18 +702,18 @@ export class CorsPreflightData {
719702
header === "authorization" &&
720703
!this.allowedHeaders.has(header)
721704
) {
722-
throw new CorsError(
705+
throw createCorsError(
723706
request.url,
724707
origin,
725-
`Header ${header} is not allowed.`,
708+
`Header ${header} is not allowed.`
726709
);
727710
}
728711

729712
if (unsafeHeaders.has(header) && !this.allowAllHeaders && !this.allowedHeaders.has(header)) {
730-
throw new CorsError(
713+
throw createCorsError(
731714
request.url,
732715
origin,
733-
`Header ${header} is not allowed.`,
716+
`Header ${header} is not allowed.`
734717
);
735718
}
736719
}

src/fetch-mocker.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
CORS_REQUEST_METHOD,
1818
CORS_REQUEST_HEADERS,
1919
CORS_ORIGIN,
20-
CorsPreflightError,
20+
createCorsPreflightError,
2121
getUnsafeHeaders,
2222
} from "./cors.js";
2323
import { createCustomRequest } from "./custom-request.js";
@@ -281,7 +281,7 @@ export class FetchMocker {
281281

282282
if (includeCredentials) {
283283
if (!preflightData.allowCredentials) {
284-
throw new CorsPreflightError(
284+
throw createCorsPreflightError(
285285
request.url,
286286
this.#baseUrl.origin,
287287
"No 'Access-Control-Allow-Credentials' header is present on the requested resource.",
@@ -454,7 +454,7 @@ export class FetchMocker {
454454

455455
// if the preflight response is successful, then we can make the actual request
456456
if (!preflightResponse.ok) {
457-
throw new CorsPreflightError(
457+
throw createCorsPreflightError(
458458
preflightRequest.url,
459459
this.#baseUrl.origin,
460460
"It does not have HTTP ok status.",

0 commit comments

Comments
 (0)