mirror of
https://github.com/denoland/deno.git
synced 2024-12-03 17:08:35 -05:00
Merge commit from fork
* fix: drop auth headers, cookies on redirect to different origin * refactor: destructure StringPrototypeEndsWith --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
60760abd87
commit
c380447de7
2 changed files with 97 additions and 1 deletions
|
@ -31,6 +31,7 @@ const {
|
|||
SafeArrayIterator,
|
||||
SafePromisePrototypeFinally,
|
||||
String,
|
||||
StringPrototypeEndsWith,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeStartsWith,
|
||||
StringPrototypeToLowerCase,
|
||||
|
@ -66,6 +67,12 @@ const REQUEST_BODY_HEADER_NAMES = [
|
|||
"content-type",
|
||||
];
|
||||
|
||||
const REDIRECT_SENSITIVE_HEADER_NAMES = [
|
||||
"authorization",
|
||||
"proxy-authorization",
|
||||
"cookie",
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {number} rid
|
||||
* @returns {Promise<{ status: number, statusText: string, headers: [string, string][], url: string, responseRid: number, error: [string, string]? }>}
|
||||
|
@ -253,12 +260,14 @@ function httpRedirectFetch(request, response, terminator) {
|
|||
if (locationHeaders.length === 0) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const currentURL = new URL(request.currentUrl());
|
||||
const locationURL = new URL(
|
||||
locationHeaders[0][1],
|
||||
response.url() ?? undefined,
|
||||
);
|
||||
if (locationURL.hash === "") {
|
||||
locationURL.hash = request.currentUrl().hash;
|
||||
locationURL.hash = currentURL.hash;
|
||||
}
|
||||
if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") {
|
||||
return networkError("Can not redirect to a non HTTP(s) url");
|
||||
|
@ -297,6 +306,28 @@ function httpRedirectFetch(request, response, terminator) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop confidential headers when redirecting to a less secure protocol
|
||||
// or to a different domain that is not a superdomain
|
||||
if (
|
||||
locationURL.protocol !== currentURL.protocol &&
|
||||
locationURL.protocol !== "https:" ||
|
||||
locationURL.host !== currentURL.host &&
|
||||
!isSubdomain(locationURL.host, currentURL.host)
|
||||
) {
|
||||
for (let i = 0; i < request.headerList.length; i++) {
|
||||
if (
|
||||
ArrayPrototypeIncludes(
|
||||
REDIRECT_SENSITIVE_HEADER_NAMES,
|
||||
byteLowerCase(request.headerList[i][0]),
|
||||
)
|
||||
) {
|
||||
ArrayPrototypeSplice(request.headerList, i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request.body !== null) {
|
||||
const res = extractBody(request.body.source);
|
||||
request.body = res.body;
|
||||
|
@ -470,6 +501,19 @@ function abortFetch(request, responseObject, error) {
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given string is a subdomain of the given domain.
|
||||
*
|
||||
* @param {String} subdomain
|
||||
* @param {String} domain
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isSubdomain(subdomain, domain) {
|
||||
const dot = subdomain.length - domain.length - 1;
|
||||
return dot > 0 && subdomain[dot] === "." &&
|
||||
StringPrototypeEndsWith(subdomain, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Response argument to the WebAssembly streaming APIs, after
|
||||
* resolving if it was passed as a promise. This function should be registered
|
||||
|
|
|
@ -439,6 +439,58 @@ Deno.test(
|
|||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
{
|
||||
permissions: { net: true },
|
||||
},
|
||||
async function fetchWithAuthorizationHeaderRedirection() {
|
||||
const response = await fetch("http://localhost:4546/echo_server", {
|
||||
headers: { authorization: "Bearer foo" },
|
||||
});
|
||||
assertEquals(response.status, 200);
|
||||
assertEquals(response.statusText, "OK");
|
||||
assertEquals(response.url, "http://localhost:4545/echo_server");
|
||||
assertEquals(response.headers.get("authorization"), null);
|
||||
assertEquals(await response.text(), "");
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
{
|
||||
permissions: { net: true },
|
||||
},
|
||||
async function fetchWithCookieHeaderRedirection() {
|
||||
const response = await fetch("http://localhost:4546/echo_server", {
|
||||
headers: { Cookie: "sessionToken=verySecret" },
|
||||
});
|
||||
assertEquals(response.status, 200);
|
||||
assertEquals(response.statusText, "OK");
|
||||
assertEquals(response.url, "http://localhost:4545/echo_server");
|
||||
assertEquals(response.headers.get("cookie"), null);
|
||||
assertEquals(await response.text(), "");
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
{
|
||||
permissions: { net: true },
|
||||
},
|
||||
async function fetchWithProxyAuthorizationHeaderRedirection() {
|
||||
const response = await fetch("http://localhost:4546/echo_server", {
|
||||
headers: {
|
||||
"proxy-authorization": "Basic ZXNwZW46a29rb3M=",
|
||||
"accept": "application/json",
|
||||
},
|
||||
});
|
||||
assertEquals(response.status, 200);
|
||||
assertEquals(response.statusText, "OK");
|
||||
assertEquals(response.url, "http://localhost:4545/echo_server");
|
||||
assertEquals(response.headers.get("proxy-authorization"), null);
|
||||
assertEquals(response.headers.get("accept"), "application/json");
|
||||
assertEquals(await response.text(), "");
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
{ permissions: { net: true } },
|
||||
async function fetchInitStringBody() {
|
||||
|
|
Loading…
Reference in a new issue