h3 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -108,10 +108,10 @@ class H3Error extends Error {
108
108
  toJSON() {
109
109
  const obj = {
110
110
  message: this.message,
111
- statusCode: this.statusCode
111
+ statusCode: sanitizeStatusCode(this.statusCode, 500)
112
112
  };
113
113
  if (this.statusMessage) {
114
- obj.statusMessage = this.statusMessage;
114
+ obj.statusMessage = sanitizeStatusMessage(this.statusMessage);
115
115
  }
116
116
  if (this.data !== void 0) {
117
117
  obj.data = this.data;
@@ -150,15 +150,24 @@ function createError(input) {
150
150
  err.data = input.data;
151
151
  }
152
152
  if (input.statusCode) {
153
- err.statusCode = input.statusCode;
153
+ err.statusCode = sanitizeStatusCode(input.statusCode, err.statusCode);
154
154
  } else if (input.status) {
155
- err.statusCode = input.status;
155
+ err.statusCode = sanitizeStatusCode(input.status, err.statusCode);
156
156
  }
157
157
  if (input.statusMessage) {
158
158
  err.statusMessage = input.statusMessage;
159
159
  } else if (input.statusText) {
160
160
  err.statusMessage = input.statusText;
161
161
  }
162
+ if (err.statusMessage) {
163
+ const originalMessage = err.statusMessage;
164
+ const sanitizedMessage = sanitizeStatusMessage(err.statusMessage);
165
+ if (sanitizedMessage !== originalMessage) {
166
+ console.warn(
167
+ "[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future `statusMessage` will be sanitized by default."
168
+ );
169
+ }
170
+ }
162
171
  if (input.fatal !== void 0) {
163
172
  err.fatal = input.fatal;
164
173
  }
@@ -446,6 +455,23 @@ function splitCookiesString(cookiesString) {
446
455
  return cookiesStrings;
447
456
  }
448
457
 
458
+ const DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g;
459
+ function sanitizeStatusMessage(statusMessage = "") {
460
+ return statusMessage.replace(DISALLOWED_STATUS_CHARS, "");
461
+ }
462
+ function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
463
+ if (!statusCode) {
464
+ return defaultStatusCode;
465
+ }
466
+ if (typeof statusCode === "string") {
467
+ statusCode = Number.parseInt(statusCode, 10);
468
+ }
469
+ if (statusCode < 100 || statusCode > 999) {
470
+ return defaultStatusCode;
471
+ }
472
+ return statusCode;
473
+ }
474
+
449
475
  const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
450
476
  const ignoredHeaders = /* @__PURE__ */ new Set([
451
477
  "transfer-encoding",
@@ -483,8 +509,11 @@ async function sendProxy(event, target, opts = {}) {
483
509
  headers: opts.headers,
484
510
  ...opts.fetchOptions
485
511
  });
486
- event.node.res.statusCode = response.status;
487
- event.node.res.statusMessage = response.statusText;
512
+ event.node.res.statusCode = sanitizeStatusCode(
513
+ response.status,
514
+ event.node.res.statusCode
515
+ );
516
+ event.node.res.statusMessage = sanitizeStatusMessage(response.statusText);
488
517
  for (const [key, value] of response.headers.entries()) {
489
518
  if (key === "content-encoding") {
490
519
  continue;
@@ -589,7 +618,7 @@ function send(event, data, type) {
589
618
  });
590
619
  }
591
620
  function sendNoContent(event, code = 204) {
592
- event.node.res.statusCode = code;
621
+ event.node.res.statusCode = sanitizeStatusCode(code, 204);
593
622
  if (event.node.res.statusCode === 204) {
594
623
  event.node.res.removeHeader("content-length");
595
624
  }
@@ -597,14 +626,13 @@ function sendNoContent(event, code = 204) {
597
626
  }
598
627
  function setResponseStatus(event, code, text) {
599
628
  if (code) {
600
- event.node.res.statusCode = code;
629
+ event.node.res.statusCode = sanitizeStatusCode(
630
+ code,
631
+ event.node.res.statusCode
632
+ );
601
633
  }
602
634
  if (text) {
603
- event.node.res.statusMessage = text.replace(
604
- // eslint-disable-next-line no-control-regex
605
- /[^\u0009\u0020-\u007E]/g,
606
- ""
607
- );
635
+ event.node.res.statusMessage = sanitizeStatusMessage(text);
608
636
  }
609
637
  }
610
638
  function getResponseStatus(event) {
@@ -619,7 +647,10 @@ function defaultContentType(event, type) {
619
647
  }
620
648
  }
621
649
  function sendRedirect(event, location, code = 302) {
622
- event.node.res.statusCode = code;
650
+ event.node.res.statusCode = sanitizeStatusCode(
651
+ code,
652
+ event.node.res.statusCode
653
+ );
623
654
  event.node.res.setHeader("location", location);
624
655
  const encodedLoc = location.replace(/"/g, "%22");
625
656
  const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`;
@@ -1070,10 +1101,13 @@ class H3Event {
1070
1101
  this.res.setHeader(key, value);
1071
1102
  }
1072
1103
  if (response.status) {
1073
- this.res.statusCode = response.status;
1104
+ this.res.statusCode = sanitizeStatusCode(
1105
+ response.status,
1106
+ this.res.statusCode
1107
+ );
1074
1108
  }
1075
1109
  if (response.statusText) {
1076
- this.res.statusMessage = response.statusText;
1110
+ this.res.statusMessage = sanitizeStatusMessage(response.statusText);
1077
1111
  }
1078
1112
  if (response.redirected) {
1079
1113
  this.res.setHeader("location", response.url);
@@ -1465,6 +1499,8 @@ exports.proxyRequest = proxyRequest;
1465
1499
  exports.readBody = readBody;
1466
1500
  exports.readMultipartFormData = readMultipartFormData;
1467
1501
  exports.readRawBody = readRawBody;
1502
+ exports.sanitizeStatusCode = sanitizeStatusCode;
1503
+ exports.sanitizeStatusMessage = sanitizeStatusMessage;
1468
1504
  exports.sealSession = sealSession;
1469
1505
  exports.send = send;
1470
1506
  exports.sendError = sendError;
package/dist/index.d.ts CHANGED
@@ -346,7 +346,7 @@ declare function send(event: H3Event, data?: any, type?: string): Promise<void>;
346
346
  * @param code status code to be send. By default, it is `204 No Content`.
347
347
  */
348
348
  declare function sendNoContent(event: H3Event, code?: number): void;
349
- declare function setResponseStatus(event: H3Event, code: number, text?: string): void;
349
+ declare function setResponseStatus(event: H3Event, code?: number, text?: string): void;
350
350
  declare function getResponseStatus(event: H3Event): number;
351
351
  declare function getResponseStatusText(event: H3Event): string;
352
352
  declare function defaultContentType(event: H3Event, type?: string): void;
@@ -384,6 +384,9 @@ declare function isCorsOriginAllowed(origin: ReturnType<typeof getRequestHeaders
384
384
  declare function appendCorsPreflightHeaders(event: H3Event, options: H3CorsOptions): void;
385
385
  declare function appendCorsHeaders(event: H3Event, options: H3CorsOptions): void;
386
386
 
387
+ declare function sanitizeStatusMessage(statusMessage?: string): string;
388
+ declare function sanitizeStatusCode(statusCode: string | number, defaultStatusCode?: number): number;
389
+
387
390
  type RouterMethod = Lowercase<HTTPMethod>;
388
391
  type RouterUse = (path: string, handler: EventHandler, method?: RouterMethod | RouterMethod[]) => Router;
389
392
  type AddRouteShortcuts = Record<RouterMethod, RouterUse>;
@@ -399,4 +402,4 @@ interface CreateRouterOptions {
399
402
  }
400
403
  declare function createRouter(opts?: CreateRouterOptions): Router;
401
404
 
402
- export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3CorsOptions, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, MultiPartData, NodeEventContext, NodeListener, NodeMiddleware, NodePromisifiedHandler, ProxyOptions, RequestHeaders, Router, RouterMethod, RouterUse, Session, SessionConfig, SessionData, Stack, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getRequestHost, getRequestProtocol, getRequestURL, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };
405
+ export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3CorsOptions, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, MultiPartData, NodeEventContext, NodeListener, NodeMiddleware, NodePromisifiedHandler, ProxyOptions, RequestHeaders, Router, RouterMethod, RouterUse, Session, SessionConfig, SessionData, Stack, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getRequestHost, getRequestProtocol, getRequestURL, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, sanitizeStatusCode, sanitizeStatusMessage, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };
package/dist/index.mjs CHANGED
@@ -106,10 +106,10 @@ class H3Error extends Error {
106
106
  toJSON() {
107
107
  const obj = {
108
108
  message: this.message,
109
- statusCode: this.statusCode
109
+ statusCode: sanitizeStatusCode(this.statusCode, 500)
110
110
  };
111
111
  if (this.statusMessage) {
112
- obj.statusMessage = this.statusMessage;
112
+ obj.statusMessage = sanitizeStatusMessage(this.statusMessage);
113
113
  }
114
114
  if (this.data !== void 0) {
115
115
  obj.data = this.data;
@@ -148,15 +148,24 @@ function createError(input) {
148
148
  err.data = input.data;
149
149
  }
150
150
  if (input.statusCode) {
151
- err.statusCode = input.statusCode;
151
+ err.statusCode = sanitizeStatusCode(input.statusCode, err.statusCode);
152
152
  } else if (input.status) {
153
- err.statusCode = input.status;
153
+ err.statusCode = sanitizeStatusCode(input.status, err.statusCode);
154
154
  }
155
155
  if (input.statusMessage) {
156
156
  err.statusMessage = input.statusMessage;
157
157
  } else if (input.statusText) {
158
158
  err.statusMessage = input.statusText;
159
159
  }
160
+ if (err.statusMessage) {
161
+ const originalMessage = err.statusMessage;
162
+ const sanitizedMessage = sanitizeStatusMessage(err.statusMessage);
163
+ if (sanitizedMessage !== originalMessage) {
164
+ console.warn(
165
+ "[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future `statusMessage` will be sanitized by default."
166
+ );
167
+ }
168
+ }
160
169
  if (input.fatal !== void 0) {
161
170
  err.fatal = input.fatal;
162
171
  }
@@ -444,6 +453,23 @@ function splitCookiesString(cookiesString) {
444
453
  return cookiesStrings;
445
454
  }
446
455
 
456
+ const DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g;
457
+ function sanitizeStatusMessage(statusMessage = "") {
458
+ return statusMessage.replace(DISALLOWED_STATUS_CHARS, "");
459
+ }
460
+ function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
461
+ if (!statusCode) {
462
+ return defaultStatusCode;
463
+ }
464
+ if (typeof statusCode === "string") {
465
+ statusCode = Number.parseInt(statusCode, 10);
466
+ }
467
+ if (statusCode < 100 || statusCode > 999) {
468
+ return defaultStatusCode;
469
+ }
470
+ return statusCode;
471
+ }
472
+
447
473
  const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
448
474
  const ignoredHeaders = /* @__PURE__ */ new Set([
449
475
  "transfer-encoding",
@@ -481,8 +507,11 @@ async function sendProxy(event, target, opts = {}) {
481
507
  headers: opts.headers,
482
508
  ...opts.fetchOptions
483
509
  });
484
- event.node.res.statusCode = response.status;
485
- event.node.res.statusMessage = response.statusText;
510
+ event.node.res.statusCode = sanitizeStatusCode(
511
+ response.status,
512
+ event.node.res.statusCode
513
+ );
514
+ event.node.res.statusMessage = sanitizeStatusMessage(response.statusText);
486
515
  for (const [key, value] of response.headers.entries()) {
487
516
  if (key === "content-encoding") {
488
517
  continue;
@@ -587,7 +616,7 @@ function send(event, data, type) {
587
616
  });
588
617
  }
589
618
  function sendNoContent(event, code = 204) {
590
- event.node.res.statusCode = code;
619
+ event.node.res.statusCode = sanitizeStatusCode(code, 204);
591
620
  if (event.node.res.statusCode === 204) {
592
621
  event.node.res.removeHeader("content-length");
593
622
  }
@@ -595,14 +624,13 @@ function sendNoContent(event, code = 204) {
595
624
  }
596
625
  function setResponseStatus(event, code, text) {
597
626
  if (code) {
598
- event.node.res.statusCode = code;
627
+ event.node.res.statusCode = sanitizeStatusCode(
628
+ code,
629
+ event.node.res.statusCode
630
+ );
599
631
  }
600
632
  if (text) {
601
- event.node.res.statusMessage = text.replace(
602
- // eslint-disable-next-line no-control-regex
603
- /[^\u0009\u0020-\u007E]/g,
604
- ""
605
- );
633
+ event.node.res.statusMessage = sanitizeStatusMessage(text);
606
634
  }
607
635
  }
608
636
  function getResponseStatus(event) {
@@ -617,7 +645,10 @@ function defaultContentType(event, type) {
617
645
  }
618
646
  }
619
647
  function sendRedirect(event, location, code = 302) {
620
- event.node.res.statusCode = code;
648
+ event.node.res.statusCode = sanitizeStatusCode(
649
+ code,
650
+ event.node.res.statusCode
651
+ );
621
652
  event.node.res.setHeader("location", location);
622
653
  const encodedLoc = location.replace(/"/g, "%22");
623
654
  const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`;
@@ -1068,10 +1099,13 @@ class H3Event {
1068
1099
  this.res.setHeader(key, value);
1069
1100
  }
1070
1101
  if (response.status) {
1071
- this.res.statusCode = response.status;
1102
+ this.res.statusCode = sanitizeStatusCode(
1103
+ response.status,
1104
+ this.res.statusCode
1105
+ );
1072
1106
  }
1073
1107
  if (response.statusText) {
1074
- this.res.statusMessage = response.statusText;
1108
+ this.res.statusMessage = sanitizeStatusMessage(response.statusText);
1075
1109
  }
1076
1110
  if (response.redirected) {
1077
1111
  this.res.setHeader("location", response.url);
@@ -1400,4 +1434,4 @@ function createRouter(opts = {}) {
1400
1434
  return router;
1401
1435
  }
1402
1436
 
1403
- export { H3Error, H3Event, H3Headers, H3Response, MIMES, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getRequestHost, getRequestProtocol, getRequestURL, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };
1437
+ export { H3Error, H3Event, H3Headers, H3Response, MIMES, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getRequestHost, getRequestProtocol, getRequestURL, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, sanitizeStatusCode, sanitizeStatusMessage, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "h3",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "Tiny JavaScript Server",
5
5
  "repository": "unjs/h3",
6
6
  "license": "MIT",