@@ -62,7 +62,7 @@ export function getFirstHeaderValue(
6262 * Validates a request.
6363 *
6464 * @param request - The incoming `Request` object to validate.
65- * @param allowedHosts - A set of allowed hostnames .
65+ * @param allowedHosts - A set of allowed hosts .
6666 * @param disableHostCheck - Whether to disable the host check.
6767 * @throws Error if any of the validated headers contain invalid values.
6868 */
@@ -71,24 +71,24 @@ export function validateRequest(
7171 allowedHosts : ReadonlySet < string > ,
7272 disableHostCheck : boolean ,
7373) : void {
74- validateHeaders ( request , allowedHosts , disableHostCheck ) ;
74+ const url = new URL ( request . url ) ;
75+ validateHeaders ( request , allowedHosts , disableHostCheck , url . protocol ) ;
7576
7677 if ( ! disableHostCheck ) {
77- validateUrl ( new URL ( request . url ) , allowedHosts ) ;
78+ validateUrl ( url , allowedHosts ) ;
7879 }
7980}
8081
8182/**
82- * Validates that the hostname of a given URL is allowed.
83+ * Validates that the host of a given URL is allowed.
8384 *
8485 * @param url - The URL object to validate.
85- * @param allowedHosts - A set of allowed hostnames .
86- * @throws Error if the hostname is not in the allowlist.
86+ * @param allowedHosts - A set of allowed hosts .
87+ * @throws Error if the host is not in the allowlist.
8788 */
8889export function validateUrl ( url : URL , allowedHosts : ReadonlySet < string > ) : void {
89- const { hostname } = url ;
90- if ( ! isHostAllowed ( hostname , allowedHosts ) ) {
91- throw new Error ( `URL with hostname "${ hostname } " is not allowed.` ) ;
90+ if ( ! isHostAllowed ( url , allowedHosts ) ) {
91+ throw new Error ( `URL with host "${ url . host } " is not allowed.` ) ;
9292 }
9393}
9494
@@ -134,74 +134,94 @@ export function sanitizeRequestHeaders(
134134 *
135135 * @param headerName - The name of the header to validate (e.g., 'host', 'x-forwarded-host').
136136 * @param headerValue - The value of the header to validate.
137- * @param allowedHosts - A set of allowed hostnames .
137+ * @param allowedHosts - A set of allowed hosts .
138138 * @throws Error if the header value is invalid or the hostname is not in the allowlist.
139139 */
140140function verifyHostAllowed (
141141 headerName : string ,
142142 headerValue : string ,
143143 allowedHosts : ReadonlySet < string > ,
144+ protocol : string ,
144145) : void {
145- const url = `http: //${ headerValue } ` ;
146+ const url = `${ protocol } //${ headerValue } ` ;
146147 if ( ! URL . canParse ( url ) ) {
147148 throw new Error ( `Header "${ headerName } " contains an invalid value and cannot be parsed.` ) ;
148149 }
149150
150- const { hostname, pathname, search, hash, username, password } = new URL ( url ) ;
151+ const parsedUrl = new URL ( url ) ;
152+ const { pathname, search, hash, username, password } = parsedUrl ;
151153 if ( pathname !== '/' || search || hash || username || password ) {
152154 throw new Error (
153155 `Header "${ headerName } " with value "${ headerValue } " contains characters that are not allowed.` ,
154156 ) ;
155157 }
156158
157- if ( ! isHostAllowed ( hostname , allowedHosts ) ) {
159+ if ( ! isHostAllowed ( parsedUrl , allowedHosts ) ) {
158160 throw new Error ( `Header "${ headerName } " with value "${ headerValue } " is not allowed.` ) ;
159161 }
160162}
161163
162164/**
163- * Checks if the hostname is allowed.
164- * @param hostname - The hostname to check.
165- * @param allowedHosts - A set of allowed hostnames .
166- * @returns `true` if the hostname is allowed, `false` otherwise.
165+ * Checks if the host is allowed.
166+ * @param url - The URL to check.
167+ * @param allowedHosts - A set of allowed hosts .
168+ * @returns `true` if the host is allowed, `false` otherwise.
167169 */
168- function isHostAllowed ( hostname : string , allowedHosts : ReadonlySet < string > ) : boolean {
169- if ( allowedHosts . has ( '*' ) || allowedHosts . has ( hostname ) ) {
170+ function isHostAllowed ( url : URL , allowedHosts : ReadonlySet < string > ) : boolean {
171+ if ( allowedHosts . has ( '*' ) || allowedHosts . has ( url . host ) ) {
170172 return true ;
171173 }
172174
173175 for ( const allowedHost of allowedHosts ) {
174- if ( ! allowedHost . startsWith ( '*.' ) ) {
175- continue ;
176- }
177-
178- const domain = allowedHost . slice ( 1 ) ;
179- if ( hostname . endsWith ( domain ) ) {
176+ if ( isAllowedHostMatch ( url , allowedHost ) ) {
180177 return true ;
181178 }
182179 }
183180
184181 return false ;
185182}
186183
184+ function isAllowedHostMatch ( url : URL , allowedHost : string ) : boolean {
185+ const wildcard = allowedHost . startsWith ( '*.' ) ;
186+ const comparableAllowedHost = wildcard ? `placeholder${ allowedHost . slice ( 1 ) } ` : allowedHost ;
187+ const allowedUrl = `${ url . protocol } //${ comparableAllowedHost } ` ;
188+
189+ if ( ! URL . canParse ( allowedUrl ) ) {
190+ return false ;
191+ }
192+
193+ const parsedAllowedUrl = new URL ( allowedUrl ) ;
194+ if ( url . port !== parsedAllowedUrl . port ) {
195+ return false ;
196+ }
197+
198+ if ( wildcard ) {
199+ const domain = parsedAllowedUrl . hostname . slice ( 'placeholder' . length ) ;
200+ return url . hostname . endsWith ( domain ) ;
201+ }
202+
203+ return url . hostname === parsedAllowedUrl . hostname ;
204+ }
205+
187206/**
188207 * Validates the headers of an incoming request.
189208 *
190209 * @param request - The incoming `Request` object containing the headers to validate.
191- * @param allowedHosts - A set of allowed hostnames .
210+ * @param allowedHosts - A set of allowed hosts .
192211 * @param disableHostCheck - Whether to disable the host check.
193212 * @throws Error if any of the validated headers contain invalid values.
194213 */
195214function validateHeaders (
196215 request : Request ,
197216 allowedHosts : ReadonlySet < string > ,
198217 disableHostCheck : boolean ,
218+ protocol : string ,
199219) : void {
200220 const headers = request . headers ;
201221 for ( const headerName of HOST_HEADERS_TO_VALIDATE ) {
202222 const headerValue = getFirstHeaderValue ( headers . get ( headerName ) ) ;
203223 if ( headerValue && ! disableHostCheck ) {
204- verifyHostAllowed ( headerName , headerValue , allowedHosts ) ;
224+ verifyHostAllowed ( headerName , headerValue , allowedHosts , protocol ) ;
205225 }
206226 }
207227
0 commit comments