From bfad444a05e3bbdcfc15ae75714587e1c26136f6 Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Sun, 11 May 2025 22:17:53 +0100 Subject: [PATCH 1/8] feat: scrub user agent --- src/main/browser/profile-manager.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/browser/profile-manager.ts b/src/main/browser/profile-manager.ts index 92634f1f..25ef91b9 100644 --- a/src/main/browser/profile-manager.ts +++ b/src/main/browser/profile-manager.ts @@ -14,6 +14,9 @@ import { registerWindow, WindowType } from "@/modules/windows"; import { getSettingValueById } from "@/saving/settings"; import { ExtensionManager } from "@/modules/extensions/management"; +const SCRUB_ELECTRON_USER_AGENT = true; +const SCRUB_APP_USER_AGENT = false; + /** * Represents a loaded browser profile */ @@ -121,10 +124,16 @@ export class ProfileManager { // Remove Electron and App details to closer emulate Chrome's UA if (FLAGS.SCRUBBED_USER_AGENT) { - const userAgent = profileSession - .getUserAgent() - .replace(/\sElectron\/\S+/, "") - .replace(new RegExp(`\\s${app.getName()}/\\S+`, "i"), ""); + let userAgent = profileSession.getUserAgent(); + + if (SCRUB_ELECTRON_USER_AGENT) { + userAgent = userAgent.replace(/\sElectron\/\S+/, ""); + } + + if (SCRUB_APP_USER_AGENT) { + userAgent = userAgent.replace(new RegExp(`\\s${app.getName()}/\\S+`, "i"), ""); + } + profileSession.setUserAgent(userAgent); } From bca09751dd58907a856a5a53d80a200e86742868 Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Sun, 11 May 2025 22:17:57 +0100 Subject: [PATCH 2/8] feat: enable scrubbing --- src/main/modules/flags.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/modules/flags.ts b/src/main/modules/flags.ts index 20f3f1a0..2e43c6ab 100644 --- a/src/main/modules/flags.ts +++ b/src/main/modules/flags.ts @@ -16,7 +16,7 @@ type Flags = { export const FLAGS: Flags = { // Disabled, because it causes cloudflare turnstile to flag us. // It also causes Google to flag us as a bot, which stops us from logging in to Google. - SCRUBBED_USER_AGENT: false, + SCRUBBED_USER_AGENT: true, // Replace - Use window.location.replace to load the error page. // Load - Add the page to the history stack by loading it normally. From 736eb060186fd05e62b247b18824899c62f0174b Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Sun, 11 May 2025 22:18:36 +0100 Subject: [PATCH 3/8] feat: add comments --- src/main/modules/flags.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/modules/flags.ts b/src/main/modules/flags.ts index 2e43c6ab..fc785d0f 100644 --- a/src/main/modules/flags.ts +++ b/src/main/modules/flags.ts @@ -16,6 +16,8 @@ type Flags = { export const FLAGS: Flags = { // Disabled, because it causes cloudflare turnstile to flag us. // It also causes Google to flag us as a bot, which stops us from logging in to Google. + // Current Solution: Remove Electron user agent, but keep app user agent. + // Don't know how long this will work though... SCRUBBED_USER_AGENT: true, // Replace - Use window.location.replace to load the error page. From f283abbc0192ab750f9e84be9bd08dc7131c37c0 Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Sun, 18 May 2025 22:40:30 +0100 Subject: [PATCH 4/8] feat: add edge --- src/main/browser/profile-manager.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/browser/profile-manager.ts b/src/main/browser/profile-manager.ts index 25ef91b9..0ca1deed 100644 --- a/src/main/browser/profile-manager.ts +++ b/src/main/browser/profile-manager.ts @@ -14,8 +14,9 @@ import { registerWindow, WindowType } from "@/modules/windows"; import { getSettingValueById } from "@/saving/settings"; import { ExtensionManager } from "@/modules/extensions/management"; -const SCRUB_ELECTRON_USER_AGENT = true; -const SCRUB_APP_USER_AGENT = false; +const REMOVE_ELECTRON_USER_AGENT = false; +const REMOVE_APP_USER_AGENT = false; +const ADD_EDGE_USER_AGENT = true; /** * Represents a loaded browser profile @@ -126,14 +127,18 @@ export class ProfileManager { if (FLAGS.SCRUBBED_USER_AGENT) { let userAgent = profileSession.getUserAgent(); - if (SCRUB_ELECTRON_USER_AGENT) { + if (REMOVE_ELECTRON_USER_AGENT) { userAgent = userAgent.replace(/\sElectron\/\S+/, ""); } - if (SCRUB_APP_USER_AGENT) { + if (REMOVE_APP_USER_AGENT) { userAgent = userAgent.replace(new RegExp(`\\s${app.getName()}/\\S+`, "i"), ""); } + if (ADD_EDGE_USER_AGENT) { + userAgent = `${userAgent} Edg/136.0.3240.76`; + } + profileSession.setUserAgent(userAgent); } From 02a5eea5dd8a8af0a22e6b3224507c706b425137 Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Mon, 19 May 2025 21:09:27 +0100 Subject: [PATCH 5/8] feat: spotify & google accounts work --- src/main/browser/utility/intercept-rules.ts | 43 ++++++++++++++------- src/main/browser/utility/user-agent.ts | 37 ++++++++++++++++++ src/main/modules/flags.ts | 2 +- 3 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 src/main/browser/utility/user-agent.ts diff --git a/src/main/browser/utility/intercept-rules.ts b/src/main/browser/utility/intercept-rules.ts index c71613cc..cd5c54f4 100644 --- a/src/main/browser/utility/intercept-rules.ts +++ b/src/main/browser/utility/intercept-rules.ts @@ -4,8 +4,33 @@ import { generateID } from "@/modules/utils"; import { getSettingValueById } from "@/saving/settings"; import { Session } from "electron"; import { URL } from "url"; +import { transformUserAgentHeader } from "@/browser/utility/user-agent"; + +function setupUserAgentTransformer(session: Session) { + const webRequest = createBetterWebRequest(session.webRequest, "user-agent-transformer"); + + webRequest.onBeforeSendHeaders((details, callback) => { + let updated = false; + + const url = URL.parse(details.url); + + const requestHeaders = details.requestHeaders; + const newHeaders = { ...requestHeaders }; + for (const header of Object.keys(requestHeaders)) { + if (header.toLowerCase() == "user-agent") { + newHeaders[header] = transformUserAgentHeader(url, requestHeaders[header]); + updated = true; + } + } + + if (updated) { + callback({ requestHeaders: newHeaders }); + } else { + callback({}); + } + }); +} -// Bypass CORS for flow and flow-internal protocols function setupCorsBypassForFlowProtocols(session: Session) { const webRequest = createBetterWebRequest(session.webRequest, "bypass-cors"); @@ -36,7 +61,6 @@ function setupCorsBypassForFlowProtocols(session: Session) { }); } -// Setup redirects required for the better PDF viewer function setupBetterPdfViewer(session: Session) { const webRequest = createBetterWebRequest(session.webRequest, "better-pdf-viewer"); @@ -86,22 +110,13 @@ function setupBetterPdfViewer(session: Session) { callback({}); } ); - - // Update Origin header to requests - webRequest.onBeforeSendHeaders((details, callback) => { - const url = details.url; - const urlObject = URL.parse(url); - if (!urlObject) { - return callback({}); - } - - const newHeaders = { ...details.requestHeaders, Origin: urlObject.origin }; - callback({ requestHeaders: newHeaders }); - }); } // Setup intercept rules for the session export function setupInterceptRules(session: Session) { + // Transform the User-Agent header + setupUserAgentTransformer(session); + // Bypass CORS for flow and flow-internal protocols setupCorsBypassForFlowProtocols(session); diff --git a/src/main/browser/utility/user-agent.ts b/src/main/browser/utility/user-agent.ts new file mode 100644 index 00000000..922ce616 --- /dev/null +++ b/src/main/browser/utility/user-agent.ts @@ -0,0 +1,37 @@ +const appName = "Flow"; + +const EDGE_USER_AGENT = "Edg/136.0.3240.76"; + +export function transformUserAgentHeader(url: URL | null, userAgent: string) { + let addEdgeUserAgent = false; + let removeElectronUserAgent = false; + let removeAppUserAgent = false; + + if (url) { + const hostname = url.hostname.toLowerCase(); + if (hostname === "accounts.google.com") { + addEdgeUserAgent = false; + removeElectronUserAgent = true; + removeAppUserAgent = false; + } + if (hostname.endsWith("spotify.com") || hostname.endsWith("spotifycdn.com")) { + addEdgeUserAgent = false; + removeElectronUserAgent = true; + removeAppUserAgent = false; + } + } + + if (removeElectronUserAgent) { + userAgent = userAgent.replace(/\sElectron\/\S+/, ""); + } + + if (removeAppUserAgent) { + userAgent = userAgent.replace(new RegExp(`\\s${appName}/\\S+`, "i"), ""); + } + + if (addEdgeUserAgent) { + userAgent = `${userAgent} ${EDGE_USER_AGENT}`; + } + + return userAgent; +} diff --git a/src/main/modules/flags.ts b/src/main/modules/flags.ts index fc785d0f..2e9bf9de 100644 --- a/src/main/modules/flags.ts +++ b/src/main/modules/flags.ts @@ -18,7 +18,7 @@ export const FLAGS: Flags = { // It also causes Google to flag us as a bot, which stops us from logging in to Google. // Current Solution: Remove Electron user agent, but keep app user agent. // Don't know how long this will work though... - SCRUBBED_USER_AGENT: true, + SCRUBBED_USER_AGENT: false, // Replace - Use window.location.replace to load the error page. // Load - Add the page to the history stack by loading it normally. From 49108204307417f9c8138893164889f8df65c0ff Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Tue, 20 May 2025 17:11:57 +0100 Subject: [PATCH 6/8] feat: intergrated user agent transformer --- src/main/browser/profile-manager.ts | 27 ++++------------- src/main/browser/utility/intercept-rules.ts | 8 +++-- src/main/browser/utility/user-agent.ts | 33 ++++++++++----------- src/main/modules/flags.ts | 7 ++--- src/preload/index.ts | 26 ++++++++++++++++ 5 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/main/browser/profile-manager.ts b/src/main/browser/profile-manager.ts index 0ca1deed..5638a766 100644 --- a/src/main/browser/profile-manager.ts +++ b/src/main/browser/profile-manager.ts @@ -1,10 +1,9 @@ -import { app, BrowserWindow, dialog, Session } from "electron"; +import { BrowserWindow, dialog, Session } from "electron"; import { getSession } from "@/browser/sessions"; import { TypedEventEmitter } from "@/modules/typed-event-emitter"; import { getProfile, getProfilePath, ProfileData } from "@/sessions/profiles"; import { BrowserEvents } from "@/browser/events"; import { Browser } from "@/browser/browser"; -import { FLAGS } from "@/modules/flags"; import { ElectronChromeExtensions } from "electron-chrome-extensions"; import { NEW_TAB_URL } from "@/browser/tabs/tab-manager"; import { ExtensionInstallStatus, installChromeWebStore } from "electron-chrome-web-store"; @@ -13,10 +12,7 @@ import { setWindowSpace } from "@/ipc/session/spaces"; import { registerWindow, WindowType } from "@/modules/windows"; import { getSettingValueById } from "@/saving/settings"; import { ExtensionManager } from "@/modules/extensions/management"; - -const REMOVE_ELECTRON_USER_AGENT = false; -const REMOVE_APP_USER_AGENT = false; -const ADD_EDGE_USER_AGENT = true; +import { transformUserAgentHeader } from "@/browser/utility/user-agent"; /** * Represents a loaded browser profile @@ -124,22 +120,11 @@ export class ProfileManager { const profilePath = getProfilePath(profileId); // Remove Electron and App details to closer emulate Chrome's UA - if (FLAGS.SCRUBBED_USER_AGENT) { - let userAgent = profileSession.getUserAgent(); - - if (REMOVE_ELECTRON_USER_AGENT) { - userAgent = userAgent.replace(/\sElectron\/\S+/, ""); - } - - if (REMOVE_APP_USER_AGENT) { - userAgent = userAgent.replace(new RegExp(`\\s${app.getName()}/\\S+`, "i"), ""); - } - - if (ADD_EDGE_USER_AGENT) { - userAgent = `${userAgent} Edg/136.0.3240.76`; - } + const oldUserAgent = profileSession.getUserAgent(); + const newUserAgent = transformUserAgentHeader(oldUserAgent, null); - profileSession.setUserAgent(userAgent); + if (oldUserAgent !== newUserAgent) { + profileSession.setUserAgent(newUserAgent); } // Setup Extensions diff --git a/src/main/browser/utility/intercept-rules.ts b/src/main/browser/utility/intercept-rules.ts index cd5c54f4..4aa5c0df 100644 --- a/src/main/browser/utility/intercept-rules.ts +++ b/src/main/browser/utility/intercept-rules.ts @@ -18,8 +18,12 @@ function setupUserAgentTransformer(session: Session) { const newHeaders = { ...requestHeaders }; for (const header of Object.keys(requestHeaders)) { if (header.toLowerCase() == "user-agent") { - newHeaders[header] = transformUserAgentHeader(url, requestHeaders[header]); - updated = true; + const oldValue = requestHeaders[header]; + const newValue = transformUserAgentHeader(oldValue, url); + if (oldValue !== newValue) { + newHeaders[header] = newValue; + updated = true; + } } } diff --git a/src/main/browser/utility/user-agent.ts b/src/main/browser/utility/user-agent.ts index 922ce616..5cb34897 100644 --- a/src/main/browser/utility/user-agent.ts +++ b/src/main/browser/utility/user-agent.ts @@ -1,24 +1,21 @@ -const appName = "Flow"; +import { FLAGS } from "@/modules/flags"; +import { app } from "electron"; const EDGE_USER_AGENT = "Edg/136.0.3240.76"; -export function transformUserAgentHeader(url: URL | null, userAgent: string) { - let addEdgeUserAgent = false; - let removeElectronUserAgent = false; - let removeAppUserAgent = false; +export function transformUserAgentHeader(userAgent: string, url: URL | null) { + if (!FLAGS.SCRUBBED_USER_AGENT) { + return userAgent; + } + + const addEdgeUserAgent = true; + const removeElectronUserAgent = true; + const removeAppUserAgent = false; if (url) { - const hostname = url.hostname.toLowerCase(); - if (hostname === "accounts.google.com") { - addEdgeUserAgent = false; - removeElectronUserAgent = true; - removeAppUserAgent = false; - } - if (hostname.endsWith("spotify.com") || hostname.endsWith("spotifycdn.com")) { - addEdgeUserAgent = false; - removeElectronUserAgent = true; - removeAppUserAgent = false; - } + // const hostname = url.hostname.toLowerCase(); + // if (hostname === "accounts.google.com") { + // } } if (removeElectronUserAgent) { @@ -26,10 +23,12 @@ export function transformUserAgentHeader(url: URL | null, userAgent: string) { } if (removeAppUserAgent) { + const appName = app.getName(); userAgent = userAgent.replace(new RegExp(`\\s${appName}/\\S+`, "i"), ""); } - if (addEdgeUserAgent) { + const hasEdgeUserAgent = userAgent.includes(EDGE_USER_AGENT); + if (addEdgeUserAgent && !hasEdgeUserAgent) { userAgent = `${userAgent} ${EDGE_USER_AGENT}`; } diff --git a/src/main/modules/flags.ts b/src/main/modules/flags.ts index 2e9bf9de..4d161717 100644 --- a/src/main/modules/flags.ts +++ b/src/main/modules/flags.ts @@ -14,11 +14,8 @@ type Flags = { }; export const FLAGS: Flags = { - // Disabled, because it causes cloudflare turnstile to flag us. - // It also causes Google to flag us as a bot, which stops us from logging in to Google. - // Current Solution: Remove Electron user agent, but keep app user agent. - // Don't know how long this will work though... - SCRUBBED_USER_AGENT: false, + // Transform the user agent + SCRUBBED_USER_AGENT: true, // Replace - Use window.location.replace to load the error page. // Load - Add the page to the history stack by loading it normally. diff --git a/src/preload/index.ts b/src/preload/index.ts index add9f307..2ea15f2c 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -88,6 +88,32 @@ if (hasPermission("browser")) { injectBrowserAction(); } +// TRANSFORM USER AGENT DATA // +const updateUserAgentData = () => { + Object.defineProperty(navigator, "userAgentData", { + get: () => { + return { + brands: [ + { + brand: "Not.A/Brand", + version: "99" + }, + { + brand: "Chromium", + version: "136" + } + ], + mobile: false, + platform: "macOS" + }; + }, + set: () => {} + }); +}; +contextBridge.executeInMainWorld({ + func: updateUserAgentData +}); + // INTERNAL FUNCTIONS // function getOSFromPlatform(platform: NodeJS.Platform) { switch (platform) { From c8ce07a6922c338c75a116ae0d0718e1b335843b Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Tue, 20 May 2025 17:14:18 +0100 Subject: [PATCH 7/8] chore: remove polyfilled navigator.userAgentData --- src/preload/index.ts | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/preload/index.ts b/src/preload/index.ts index 2ea15f2c..add9f307 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -88,32 +88,6 @@ if (hasPermission("browser")) { injectBrowserAction(); } -// TRANSFORM USER AGENT DATA // -const updateUserAgentData = () => { - Object.defineProperty(navigator, "userAgentData", { - get: () => { - return { - brands: [ - { - brand: "Not.A/Brand", - version: "99" - }, - { - brand: "Chromium", - version: "136" - } - ], - mobile: false, - platform: "macOS" - }; - }, - set: () => {} - }); -}; -contextBridge.executeInMainWorld({ - func: updateUserAgentData -}); - // INTERNAL FUNCTIONS // function getOSFromPlatform(platform: NodeJS.Platform) { switch (platform) { From f7fcf4cf48d3d0350e6f466d4f374b591d25aa5e Mon Sep 17 00:00:00 2001 From: iamEvan <47493765+iamEvanYT@users.noreply.github.com> Date: Tue, 20 May 2025 17:23:26 +0100 Subject: [PATCH 8/8] remove: edge user agent --- src/main/browser/utility/user-agent.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/browser/utility/user-agent.ts b/src/main/browser/utility/user-agent.ts index 5cb34897..2d8482db 100644 --- a/src/main/browser/utility/user-agent.ts +++ b/src/main/browser/utility/user-agent.ts @@ -8,8 +8,16 @@ export function transformUserAgentHeader(userAgent: string, url: URL | null) { return userAgent; } - const addEdgeUserAgent = true; + // Edge User Agent: + // - Causes a few issues with Google Auth + const addEdgeUserAgent = false; + + // Remove Electron User Agent: + // - Causes issues with Spotify const removeElectronUserAgent = true; + + // Remove App User Agent: + // - Flow will be less identifiable const removeAppUserAgent = false; if (url) {