@@ -13,16 +13,29 @@ import https from 'https';
1313import zlib from 'zlib' ;
1414import Stream from 'stream' ;
1515
16- import Body , { writeToStream , getTotalBytes } from './body' ;
17- import Response from './response' ;
18- import Headers , { createHeadersLenient } from './headers' ;
19- import Request , { getNodeRequestOptions } from './request' ;
20- import FetchError from './fetch-error' ;
21- import AbortError from './abort-error' ;
16+ import Body , { writeToStream , getTotalBytes } from './body.js' ;
17+ import Response from './response.js' ;
18+ import Headers , { createHeadersLenient } from './headers.js' ;
19+ import Request , { getNodeRequestOptions } from './request.js' ;
20+ import FetchError from './fetch-error.js' ;
21+ import AbortError from './abort-error.js' ;
22+
23+ import whatwgUrl from 'whatwg-url' ;
24+
25+ const URL = Url . URL || whatwgUrl . URL ;
2226
2327// fix an issue where "PassThrough", "resolve" aren't a named export for node <10
2428const PassThrough = Stream . PassThrough ;
25- const resolve_url = Url . resolve ;
29+
30+ const isDomainOrSubdomain = ( destination , original ) => {
31+ const orig = new URL ( original ) . hostname ;
32+ const dest = new URL ( destination ) . hostname ;
33+
34+ return orig === dest || (
35+ orig [ orig . length - dest . length - 1 ] === '.' && orig . endsWith ( dest )
36+ ) ;
37+ } ;
38+
2639
2740/**
2841 * Fetch function
@@ -109,7 +122,19 @@ export default function fetch(url, opts) {
109122 const location = headers . get ( 'Location' ) ;
110123
111124 // HTTP fetch step 5.3
112- const locationURL = location === null ? null : resolve_url ( request . url , location ) ;
125+ let locationURL = null ;
126+ try {
127+ locationURL = location === null ? null : new URL ( location , request . url ) . toString ( ) ;
128+ } catch ( err ) {
129+ // error here can only be invalid URL in Location: header
130+ // do not throw when options.redirect == manual
131+ // let the user extract the errorneous redirect URL
132+ if ( request . redirect !== 'manual' ) {
133+ reject ( new FetchError ( `uri requested responds with an invalid redirect URL: ${ location } ` , 'invalid-redirect' ) ) ;
134+ finalize ( ) ;
135+ return ;
136+ }
137+ }
113138
114139 // HTTP fetch step 5.5
115140 switch ( request . redirect ) {
@@ -154,9 +179,15 @@ export default function fetch(url, opts) {
154179 body : request . body ,
155180 signal : request . signal ,
156181 timeout : request . timeout ,
157- size : request . size
182+ size : request . size
158183 } ;
159184
185+ if ( ! isDomainOrSubdomain ( request . url , locationURL ) ) {
186+ for ( const name of [ 'authorization' , 'www-authenticate' , 'cookie' , 'cookie2' ] ) {
187+ requestOpts . headers . delete ( name ) ;
188+ }
189+ }
190+
160191 // HTTP-redirect fetch step 9
161192 if ( res . statusCode !== 303 && request . body && getTotalBytes ( request ) === null ) {
162193 reject ( new FetchError ( 'Cannot follow redirect with body being a readable stream' , 'unsupported-redirect' ) ) ;
0 commit comments