aboutsummaryrefslogtreecommitdiffstats
path: root/packages/main/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-24 19:00:07 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-24 19:13:56 +0100
commit90471302835dad5251ea568091cfd6c21d757fd3 (patch)
tree66ba2d5181ef4c4cb552c53936369bcb530c524c /packages/main/src
parentfix: Typings in js config files (diff)
downloadsophie-90471302835dad5251ea568091cfd6c21d757fd3.tar.gz
sophie-90471302835dad5251ea568091cfd6c21d757fd3.tar.zst
sophie-90471302835dad5251ea568091cfd6c21d757fd3.zip
feat: User agent data simulator
Diffstat (limited to 'packages/main/src')
-rw-r--r--packages/main/src/index.ts56
-rw-r--r--packages/main/src/userAgent.ts61
2 files changed, 51 insertions, 66 deletions
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts
index b6df67b..70acb15 100644
--- a/packages/main/src/index.ts
+++ b/packages/main/src/index.ts
@@ -19,6 +19,7 @@
19 */ 19 */
20 20
21import { app, BrowserView, BrowserWindow } from 'electron'; 21import { app, BrowserView, BrowserWindow } from 'electron';
22import { readFile, readFileSync } from 'fs';
22import { autorun } from 'mobx'; 23import { autorun } from 'mobx';
23import { getSnapshot, onPatch } from 'mobx-state-tree'; 24import { getSnapshot, onPatch } from 'mobx-state-tree';
24import { join } from 'path'; 25import { join } from 'path';
@@ -39,7 +40,6 @@ import {
39 openDevToolsWhenReady, 40 openDevToolsWhenReady,
40} from './devTools'; 41} from './devTools';
41import { createRootStore } from './stores/RootStore'; 42import { createRootStore } from './stores/RootStore';
42import { reduceUserAgent } from './userAgent';
43 43
44const isDevelopment = import.meta.env.MODE === 'development'; 44const isDevelopment = import.meta.env.MODE === 'development';
45 45
@@ -67,12 +67,33 @@ app.commandLine.appendSwitch(
67 67
68// Remove sophie and electron from the user-agent string to avoid detection. 68// Remove sophie and electron from the user-agent string to avoid detection.
69const originalUserAgent = app.userAgentFallback; 69const originalUserAgent = app.userAgentFallback;
70const userAgent = reduceUserAgent(originalUserAgent); 70const userAgent = originalUserAgent.replaceAll(/\s(sophie|Electron)\/\S+/g, '');
71const platformInUa = userAgent.match(/\((Win|Mac|X11; L)/);
72let platform = 'Unknown';
73if (platformInUa !== null) {
74 switch (platformInUa[1]) {
75 case 'Win':
76 platform = 'Windows';
77 break;
78 case 'Mac':
79 platform = 'macOS';
80 break;
81 case 'X11; L':
82 platform = 'Linux';
83 break;
84 }
85}
86const chromiumVersion = process.versions.chrome.split('.')[0];
71// Removing the electron version breaks redux devtools, so we only do this in production. 87// Removing the electron version breaks redux devtools, so we only do this in production.
72if (!isDevelopment) { 88if (!isDevelopment) {
73 app.userAgentFallback = userAgent; 89 app.userAgentFallback = userAgent;
74} 90}
75 91
92let serviceInjectRelativePath = '../../service-inject/dist/index.cjs';
93let serviceInjectPath = join(__dirname, serviceInjectRelativePath);
94let serviceInjectUrl = new URL(serviceInjectRelativePath, `file://${__dirname}`).toString();
95let serviceInject: string = readFileSync(serviceInjectPath, 'utf8');
96
76if (isDevelopment) { 97if (isDevelopment) {
77 installDevToolsExtensions(app); 98 installDevToolsExtensions(app);
78} 99}
@@ -92,7 +113,7 @@ function createWindow(): Promise<unknown> {
92 }); 113 });
93 114
94 if (isDevelopment) { 115 if (isDevelopment) {
95 openDevToolsWhenReady(mainWindow); 116 //openDevToolsWhenReady(mainWindow);
96 } 117 }
97 118
98 mainWindow.on('ready-to-show', () => { 119 mainWindow.on('ready-to-show', () => {
@@ -131,7 +152,14 @@ function createWindow(): Promise<unknown> {
131 store.setPaletteMode(paletteMode.parse(args[0])) 152 store.setPaletteMode(paletteMode.parse(args[0]))
132 break; 153 break;
133 case RendererToMainIpcMessage.ReloadAllServices: 154 case RendererToMainIpcMessage.ReloadAllServices:
134 browserView.webContents.reload(); 155 readFile(serviceInjectPath, 'utf8', (err, data) => {
156 if (err === null) {
157 serviceInject = data;
158 } else {
159 console.error('Error while reloading', serviceInjectPath, err);
160 }
161 browserView.webContents.reload();
162 });
135 break; 163 break;
136 default: 164 default:
137 console.error('Unknown IPC message:', channel, args); 165 console.error('Unknown IPC message:', channel, args);
@@ -149,6 +177,16 @@ function createWindow(): Promise<unknown> {
149 browserView.webContents.on('ipc-message', (_event, channel, ...args) => { 177 browserView.webContents.on('ipc-message', (_event, channel, ...args) => {
150 try { 178 try {
151 switch (channel) { 179 switch (channel) {
180 case ServiceToMainIpcMessage.ApiExposedInMainWorld:
181 browserView.webContents.executeJavaScriptInIsolatedWorld(0, [
182 {
183 code: serviceInject,
184 url: serviceInjectUrl,
185 }
186 ]).catch((err) => {
187 console.error('Cannot inject script:', err);
188 });
189 break;
152 case ServiceToMainIpcMessage.SetUnreadCount: 190 case ServiceToMainIpcMessage.SetUnreadCount:
153 console.log('Unread count:', unreadCount.parse(args[0])); 191 console.log('Unread count:', unreadCount.parse(args[0]));
154 break; 192 break;
@@ -172,13 +210,21 @@ function createWindow(): Promise<unknown> {
172 ); 210 );
173 }); 211 });
174 212
213 browserView.webContents.session.webRequest.onBeforeSendHeaders(({ requestHeaders }, callback) => {
214 requestHeaders['User-Agent'] = userAgent;
215 requestHeaders['Sec-CH-UA'] = `" Not A;Brand";v="99", "Chromium";v="${chromiumVersion}"`;
216 requestHeaders['Sec-CH-UA-Mobile'] = '?0';
217 requestHeaders['Sec-CH-UA-Platform'] = platform;
218 callback({ requestHeaders });
219 });
220
175 const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined) 221 const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined)
176 ? import.meta.env.VITE_DEV_SERVER_URL 222 ? import.meta.env.VITE_DEV_SERVER_URL
177 : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString(); 223 : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString();
178 224
179 return Promise.all([ 225 return Promise.all([
180 mainWindow.loadURL(pageUrl), 226 mainWindow.loadURL(pageUrl),
181 browserView.webContents.loadURL('https://git.marussy.com/sophie/about'), 227 browserView.webContents.loadURL('https://gmail.com').then(() => browserView.webContents.openDevTools()),
182 ]); 228 ]);
183} 229}
184 230
diff --git a/packages/main/src/userAgent.ts b/packages/main/src/userAgent.ts
deleted file mode 100644
index 298e565..0000000
--- a/packages/main/src/userAgent.ts
+++ /dev/null
@@ -1,61 +0,0 @@
1/*
2 * Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21/**
22 * @file Based on the javascript code snippet available at
23 * https://github.com/GoogleChrome/developer.chrome.com/blob/c33f7f4f2964e7deb28aee44de745c3725b70063/site/en/docs/privacy-sandbox/user-agent/snippets/index.md
24 *
25 * Used under the Apache 2.0 license according to
26 * https://github.com/GoogleChrome/developer.chrome.com/blob/c33f7f4f2964e7deb28aee44de745c3725b70063/LICENSE
27 */
28
29const electronUAParts = / (sophie|Electron)\/[^\s]+/g;
30const chromeUAs = /^Mozilla\/5\.0 \(((?<platform>Lin|Win|Mac|X11; C|X11; L)+[^\)]+)\) AppleWebKit\/537.36 \(KHTML, like Gecko\) Chrome\/(?<major>\d+)[\d\.]+(?<mobile>[ Mobile]*) Safari\/537\.36$/;
31const unifiedPlatform = {
32 'Lin': 'Linux; Android 10; K',
33 'Win': 'Windows NT 10.0; Win64; x64',
34 'Mac': 'Macintosh; Intel Mac OS X 10_15_7',
35 'X11; C': 'X11; CrOS x86_64',
36 'X11; L': 'X11; Linux x86_64',
37};
38
39/**
40 * Reduces the information exposed in the user-agent string.
41 *
42 * @param userAgent The original user-agent string.
43 * @returns The reduces user-agent string.
44 * @see https://developer.chrome.com/docs/privacy-sandbox/user-agent/
45 */
46export function reduceUserAgent(userAgent: string): string {
47 const userAgentWithoutElectron = userAgent.replaceAll(electronUAParts, '');
48 const matched = chromeUAs.exec(userAgentWithoutElectron) as unknown as {
49 groups: {
50 platform: 'Lin' | 'Win' | 'Mac' | 'X11; C' | 'X11; L',
51 major: string,
52 mobile: string,
53 }
54 };
55 if (matched) {
56 return `Mozilla/5.0 (${unifiedPlatform[matched.groups.platform]}) ` +
57 `AppleWebKit/537.36 (KHTML, like Gecko) ` +
58 `Chrome/${matched.groups.major}.0.0.0${matched.groups.mobile} Safari/537.36`
59 }
60 return userAgentWithoutElectron;
61}