mirror of
https://github.com/denoland/deno.git
synced 2024-12-12 02:27:46 -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,
|
SafeArrayIterator,
|
||||||
SafePromisePrototypeFinally,
|
SafePromisePrototypeFinally,
|
||||||
String,
|
String,
|
||||||
|
StringPrototypeEndsWith,
|
||||||
StringPrototypeSlice,
|
StringPrototypeSlice,
|
||||||
StringPrototypeStartsWith,
|
StringPrototypeStartsWith,
|
||||||
StringPrototypeToLowerCase,
|
StringPrototypeToLowerCase,
|
||||||
|
@ -66,6 +67,12 @@ const REQUEST_BODY_HEADER_NAMES = [
|
||||||
"content-type",
|
"content-type",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const REDIRECT_SENSITIVE_HEADER_NAMES = [
|
||||||
|
"authorization",
|
||||||
|
"proxy-authorization",
|
||||||
|
"cookie",
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} rid
|
* @param {number} rid
|
||||||
* @returns {Promise<{ status: number, statusText: string, headers: [string, string][], url: string, responseRid: number, error: [string, string]? }>}
|
* @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) {
|
if (locationHeaders.length === 0) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentURL = new URL(request.currentUrl());
|
||||||
const locationURL = new URL(
|
const locationURL = new URL(
|
||||||
locationHeaders[0][1],
|
locationHeaders[0][1],
|
||||||
response.url() ?? undefined,
|
response.url() ?? undefined,
|
||||||
);
|
);
|
||||||
if (locationURL.hash === "") {
|
if (locationURL.hash === "") {
|
||||||
locationURL.hash = request.currentUrl().hash;
|
locationURL.hash = currentURL.hash;
|
||||||
}
|
}
|
||||||
if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") {
|
if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") {
|
||||||
return networkError("Can not redirect to a non HTTP(s) url");
|
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) {
|
if (request.body !== null) {
|
||||||
const res = extractBody(request.body.source);
|
const res = extractBody(request.body.source);
|
||||||
request.body = res.body;
|
request.body = res.body;
|
||||||
|
@ -470,6 +501,19 @@ function abortFetch(request, responseObject, error) {
|
||||||
return 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
|
* Handle the Response argument to the WebAssembly streaming APIs, after
|
||||||
* resolving if it was passed as a promise. This function should be registered
|
* 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(
|
Deno.test(
|
||||||
{ permissions: { net: true } },
|
{ permissions: { net: true } },
|
||||||
async function fetchInitStringBody() {
|
async function fetchInitStringBody() {
|
||||||
|
|
Loading…
Reference in a new issue