+83
-0
@@ -69,2 +69,67 @@ 'use strict'; | ||
| // Credential-bearing headers that must not be forwarded across an origin | ||
| // boundary when following a redirect, to avoid leaking them to a third party. | ||
| var CROSS_ORIGIN_SENSITIVE_HEADERS = [ 'authorization', 'cookie', 'proxy-authorization' ]; | ||
| // Build an absolute href good enough to compute the origin. `currentUrl` may be | ||
| // a parsed URL object (with href) or an http options-style object built from | ||
| // { host/hostname, port, path }, which has no href. | ||
| function resolveOriginHref(currentUrl) { | ||
| if (currentUrl.href) { | ||
| return currentUrl.href; | ||
| } | ||
| var host = currentUrl.host || currentUrl.hostname; | ||
| if (!host) { | ||
| return null; | ||
| } | ||
| var protocol = currentUrl.protocol || 'http:'; | ||
| if (protocol.charAt(protocol.length - 1) !== ':') { | ||
| protocol += ':'; | ||
| } | ||
| if (currentUrl.port && String(host).indexOf(':') === -1) { | ||
| host += ':' + currentUrl.port; | ||
| } | ||
| return protocol + '//' + host + (currentUrl.path || currentUrl.pathname || '/'); | ||
| } | ||
| function isCrossOriginRedirect(currentUrl, toUrl) { | ||
| try { | ||
| var fromHref = resolveOriginHref(currentUrl); | ||
| if (!fromHref) { | ||
| // origin cannot be determined: fail closed | ||
| return true; | ||
| } | ||
| if (URL) { | ||
| return new URL(fromHref).origin !== new URL(toUrl, fromHref).origin; | ||
| } | ||
| // Fallback for runtimes without the WHATWG URL global. | ||
| // urlutil.parse does not normalize default ports, so compare protocol + | ||
| // hostname + normalized port instead of the raw `host`. | ||
| var from = urlutil.parse(fromHref); | ||
| var to = urlutil.parse(urlutil.resolve(fromHref, toUrl)); | ||
| var fromPort = from.port || (from.protocol === 'https:' ? '443' : '80'); | ||
| var toPort = to.port || (to.protocol === 'https:' ? '443' : '80'); | ||
| return from.protocol !== to.protocol || from.hostname !== to.hostname || fromPort !== toPort; | ||
| } catch (err) { | ||
| // Fail closed: if the origin cannot be determined, treat it as cross-origin | ||
| // so credentials are stripped rather than leaked. | ||
| return true; | ||
| } | ||
| } | ||
| function cleanCrossOriginHeaders(headers, sensitive) { | ||
| var list = sensitive || CROSS_ORIGIN_SENSITIVE_HEADERS; | ||
| var cleaned = {}; | ||
| if (headers) { | ||
| var names = utility.getOwnEnumerables(headers, true); | ||
| for (var i = 0; i < names.length; i++) { | ||
| var name = names[i]; | ||
| if (list.indexOf(name.toLowerCase()) === -1) { | ||
| cleaned[name] = headers[name]; | ||
| } | ||
| } | ||
| } | ||
| return cleaned; | ||
| } | ||
| // Keep-Alive: timeout=5, max=100 | ||
@@ -692,2 +757,20 @@ var KEEP_ALIVE_RE = /^timeout=(\d+)/i; | ||
| } | ||
| // do not forward credential-bearing headers / auth to a different origin | ||
| if (isCrossOriginRedirect(parsedUrl, newUrl)) { | ||
| // Clone so this redirect-specific sanitization never corrupts the | ||
| // caller's reusable options object. | ||
| var redirectArgs = Object.assign({}, args); | ||
| var sensitiveHeaders = CROSS_ORIGIN_SENSITIVE_HEADERS; | ||
| if (proxyTunnelAgent) { | ||
| // Proxy-Authorization authenticates the (unchanged) proxy hop, not | ||
| // the redirected origin, so keep it when the request is proxied. | ||
| sensitiveHeaders = sensitiveHeaders.filter(function (name) { | ||
| return name !== 'proxy-authorization'; | ||
| }); | ||
| } | ||
| redirectArgs.headers = cleanCrossOriginHeaders(args.headers || options.headers, sensitiveHeaders); | ||
| redirectArgs.auth = null; | ||
| redirectArgs.digestAuth = null; | ||
| args = redirectArgs; | ||
| } | ||
| // avoid done will be execute in the future change. | ||
@@ -694,0 +777,0 @@ var cb = callback; |
+1
-1
| { | ||
| "name": "urllib", | ||
| "version": "2.44.0", | ||
| "version": "2.44.1", | ||
| "publishConfig": { | ||
@@ -5,0 +5,0 @@ "tag": "latest-2" |
91727
3.79%1777
4.65%