diff options
author | 2021-12-25 00:52:49 +0100 | |
---|---|---|
committer | 2021-12-25 00:52:49 +0100 | |
commit | 9f0cd0d2c312519b8741ad557cd514ab1921eeb0 (patch) | |
tree | 20b586a95576b74984e57901691e9dc481124658 /packages/main/src/index.ts | |
parent | refactor: Simplify script injection (diff) | |
download | sophie-9f0cd0d2c312519b8741ad557cd514ab1921eeb0.tar.gz sophie-9f0cd0d2c312519b8741ad557cd514ab1921eeb0.tar.zst sophie-9f0cd0d2c312519b8741ad557cd514ab1921eeb0.zip |
feat: Harden window navigation and permissions
Diffstat (limited to 'packages/main/src/index.ts')
-rw-r--r-- | packages/main/src/index.ts | 60 |
1 files changed, 55 insertions, 5 deletions
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index 0c0a585..09441ef 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts | |||
@@ -84,8 +84,9 @@ function getResourcePath(relativePath: string): string { | |||
84 | return join(__dirname, relativePath); | 84 | return join(__dirname, relativePath); |
85 | } | 85 | } |
86 | 86 | ||
87 | const baseUrl = `file://${__dirname}`; | ||
87 | function getResourceUrl(relativePath: string): string { | 88 | function getResourceUrl(relativePath: string): string { |
88 | return new URL(relativePath, `file://${__dirname}`).toString(); | 89 | return new URL(relativePath, baseUrl).toString(); |
89 | } | 90 | } |
90 | 91 | ||
91 | let serviceInjectRelativePath = '../../service-inject/dist/index.cjs'; | 92 | let serviceInjectRelativePath = '../../service-inject/dist/index.cjs'; |
@@ -103,6 +104,33 @@ let mainWindow: BrowserWindow | null = null; | |||
103 | 104 | ||
104 | const store = createRootStore(); | 105 | const store = createRootStore(); |
105 | 106 | ||
107 | const rendererBaseUrl = getResourceUrl('../renderer/'); | ||
108 | function shouldCancelMainWindowRequest(url: string, method: string): boolean { | ||
109 | if (method !== 'GET') { | ||
110 | return true; | ||
111 | } | ||
112 | let normalizedUrl: string; | ||
113 | try { | ||
114 | normalizedUrl = new URL(url).toString(); | ||
115 | } catch (_err) { | ||
116 | return true; | ||
117 | } | ||
118 | if (normalizedUrl.startsWith('devtools:')) { | ||
119 | return false; | ||
120 | } | ||
121 | if (isDevelopment) { | ||
122 | if (normalizedUrl.startsWith('chrome-extension:')) { | ||
123 | return false; | ||
124 | } | ||
125 | if (import.meta.env.VITE_DEV_SERVER_URL !== undefined) { | ||
126 | const isHttp = normalizedUrl.startsWith(import.meta.env.VITE_DEV_SERVER_URL); | ||
127 | const isWs = normalizedUrl.startsWith(import.meta.env.VITE_DEV_SERVER_URL.replace(/^http:/, 'ws:')); | ||
128 | return !isHttp && !isWs; | ||
129 | } | ||
130 | } | ||
131 | return !normalizedUrl.startsWith(getResourceUrl(rendererBaseUrl)); | ||
132 | } | ||
133 | |||
106 | function createWindow(): Promise<unknown> { | 134 | function createWindow(): Promise<unknown> { |
107 | mainWindow = new BrowserWindow({ | 135 | mainWindow = new BrowserWindow({ |
108 | show: false, | 136 | show: false, |
@@ -112,6 +140,26 @@ function createWindow(): Promise<unknown> { | |||
112 | }, | 140 | }, |
113 | }); | 141 | }); |
114 | 142 | ||
143 | const { webContents } = mainWindow; | ||
144 | |||
145 | webContents.userAgent = originalUserAgent; | ||
146 | |||
147 | webContents.session.setPermissionRequestHandler((_webContents, _permission, callback) => { | ||
148 | callback(false); | ||
149 | }); | ||
150 | |||
151 | webContents.session.webRequest.onBeforeRequest(({ url, method }, callback) => { | ||
152 | callback({ | ||
153 | cancel: shouldCancelMainWindowRequest(url, method), | ||
154 | }) | ||
155 | }); | ||
156 | |||
157 | webContents.on('will-navigate', (event) => { | ||
158 | event.preventDefault(); | ||
159 | }); | ||
160 | |||
161 | webContents.setWindowOpenHandler(() => ({ action: 'deny' })); | ||
162 | |||
115 | if (isDevelopment) { | 163 | if (isDevelopment) { |
116 | openDevToolsWhenReady(mainWindow); | 164 | openDevToolsWhenReady(mainWindow); |
117 | } | 165 | } |
@@ -120,10 +168,6 @@ function createWindow(): Promise<unknown> { | |||
120 | mainWindow?.show(); | 168 | mainWindow?.show(); |
121 | }); | 169 | }); |
122 | 170 | ||
123 | const { webContents } = mainWindow; | ||
124 | |||
125 | webContents.userAgent = originalUserAgent; | ||
126 | |||
127 | const browserView = new BrowserView({ | 171 | const browserView = new BrowserView({ |
128 | webPreferences: { | 172 | webPreferences: { |
129 | sandbox: true, | 173 | sandbox: true, |
@@ -198,6 +242,12 @@ function createWindow(): Promise<unknown> { | |||
198 | } | 242 | } |
199 | }); | 243 | }); |
200 | 244 | ||
245 | browserView.webContents.session.setPermissionRequestHandler( | ||
246 | (_webContents, _permission, callback) => { | ||
247 | callback(false); | ||
248 | } | ||
249 | ); | ||
250 | |||
201 | browserView.webContents.session.webRequest.onBeforeSendHeaders(({ url, requestHeaders }, callback) => { | 251 | browserView.webContents.session.webRequest.onBeforeSendHeaders(({ url, requestHeaders }, callback) => { |
202 | if (url.match(/^[^:]+:\/\/accounts\.google\.[^.\/]+\//)) { | 252 | if (url.match(/^[^:]+:\/\/accounts\.google\.[^.\/]+\//)) { |
203 | requestHeaders['User-Agent'] = chromelessUserAgent; | 253 | requestHeaders['User-Agent'] = chromelessUserAgent; |