Skip to content

Commit

Permalink
tweak "src/electron-main/web-request" code
Browse files Browse the repository at this point in the history
* mostly refactoring/renaming
  • Loading branch information
vladimiry committed Feb 21, 2022
1 parent 71f5a0e commit ba64627
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "electron-mail",
"description": "Unofficial ProtonMail Desktop App",
"version": "4.13.3",
"version": "4.13.4",
"author": "Vladimir Yakovlev <[email protected]>",
"license": "GPL-3.0",
"homepage": "https://github.com/vladimiry/ElectronMail",
Expand Down
19 changes: 10 additions & 9 deletions src/electron-main/web-request/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from "src/shared/util";
import {Context} from "src/electron-main/model";
import {CorsProxy} from "./model";
import {getHeader, patchResponseHeaders, resolveCorsProxy} from "./service";
import {getHeader, patchCorsResponseHeaders, patchSameSiteCookieRecord, resolveCorsProxy} from "./service";
import {HEADERS} from "./const";
import {IPC_MAIN_API_NOTIFICATION$} from "src/electron-main/api/constants";
import {IPC_MAIN_API_NOTIFICATION_ACTIONS} from "src/shared/api/main-process/actions";
Expand Down Expand Up @@ -210,22 +210,23 @@ export function initWebRequestListenersByAccount(
// according to electron docs "only the last attached listener will be used", so no need to unsubscribe previously registered handlers
session.webRequest.onHeadersReceived(
(details, callback) => {
const requestProxy = requestProxyCache.get(details);
const {responseHeaders} = details;
const corsProxy = requestProxyCache.get(details)?.corsProxy;

if (!requestProxy) {
requestProxyCache.remove(details);

if (!responseHeaders) {
callback({});
return;
}

const {corsProxy} = requestProxy;

if (corsProxy) {
callback({responseHeaders: patchResponseHeaders({responseHeaders: details.responseHeaders, corsProxy})});
} else {
callback({});
patchCorsResponseHeaders(responseHeaders, corsProxy);
}

requestProxyCache.remove(details);
patchSameSiteCookieRecord(responseHeaders);

callback({responseHeaders});
},
);

Expand Down
65 changes: 33 additions & 32 deletions src/electron-main/web-request/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,10 @@ export function patchResponseHeader(
// TODO consider doing initial preflight/OPTIONS call to https://mail.protonmail.com
// and then pick all the "Access-Control-*" header names as a template instead of hardcoding the default headers
// since over time the server may start giving other headers
export const patchResponseHeaders: (
arg: { corsProxy: CorsProxy; responseHeaders: OnHeadersReceivedListenerDetails["responseHeaders"] }
) => OnHeadersReceivedListenerDetails["responseHeaders"] = ({corsProxy, responseHeaders}) => {
if (!responseHeaders) {
return responseHeaders;
}

export const patchCorsResponseHeaders: (
responseHeaders: Exclude<OnHeadersReceivedListenerDetails["responseHeaders"], undefined>,
corsProxy: CorsProxy,
) => void = (responseHeaders, corsProxy) => {
patchResponseHeader(
responseHeaders,
{
Expand Down Expand Up @@ -144,34 +141,38 @@ export const patchResponseHeaders: (
values: ["Date"],
},
);
};

{
// starting from @electron v12 (more exactly from the respective @chromium version)
// the "set-cookie" records with "samesite=strict" get blocked by @chromium, for example the "/api/auth/cookies" request case
// so to workaround the issue we replace the "samesite=strict|lax"-like attribute with "samesite=none"
for (const cookieName of Object.keys(responseHeaders)) {
if (cookieName.toLowerCase() !== "set-cookie") {
continue;
}
const cookies = responseHeaders[cookieName];
if (cookies) {
// TODO consider patching the "samesite" cookie attribute only for "/api/auth/cookies" request
responseHeaders[cookieName] = cookies.map((cookieValue) => {
if ((/samesite[\s]*=[\s]*(strict|lax|none)/i).test(cookieValue)) {
cookieValue = cookieValue.replace(/samesite[\s]*=[\s]*(strict|lax)/i, "samesite=none");
} else {
cookieValue = `${cookieValue}; samesite=none`;
}
cookieValue = /(;[\s]*secure)|(secure[\s]*;)/i.test(cookieValue)
? cookieValue
: `${cookieValue}; secure`; // "samesite=none" attribute requires "secure" attribute
return cookieValue;
});
}
export const patchSameSiteCookieRecord = (
responseHeaders: Exclude<OnHeadersReceivedListenerDetails["responseHeaders"], undefined>,
) => {
// starting from @electron v12 (more exactly from the respective @chromium version)
// the "set-cookie" records with "samesite=strict" get blocked by @chromium, for example the "/api/auth/cookies" request case
// so to workaround the issue we replace the "samesite=strict|lax"-like attribute with "samesite=none"
for (const headerName of Object.keys(responseHeaders)) {
if (headerName.toLowerCase() !== "set-cookie") {
continue;
}

const headerValues = responseHeaders[headerName];

if (!headerValues) {
continue;
}
}

return responseHeaders;
// TODO consider patching the "samesite" cookie attribute only for "/api/auth/cookies" request
responseHeaders[headerName] = headerValues.map((headerValue) => {
if ((/samesite[\s]*=[\s]*(strict|lax|none)/i).test(headerValue)) {
headerValue = headerValue.replace(/samesite[\s]*=[\s]*(strict|lax)/i, "samesite=none");
} else {
headerValue = `${headerValue}; samesite=none`;
}
headerValue = /(;[\s]*secure)|(secure[\s]*;)/i.test(headerValue)
? headerValue
: `${headerValue}; secure`; // "samesite=none" attribute requires "secure" attribute
return headerValue;
});
}
};

// TODO consider resolving/returning the proxy only for URLs with `entryApiUrl`-like origin
Expand Down

0 comments on commit ba64627

Please sign in to comment.