1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
/*
* Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
*
* This file is part of Sophie.
*
* Sophie is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { URL } from 'node:url';
import type { Session } from 'electron';
import getLogger from '../../../utils/getLogger';
import type Resources from '../../resources/Resources';
/**
* URL prefixes Sophie is allowed load in dev mode.
*
* In dev mode, in addition to the application itself,
* Sophie must be able do download and load the devtools and related extensions,
* so we have to make exceptions in the UI process request filter.
*/
export const DEVMODE_ALLOWED_URL_PREFIXES = [
'chrome-extension:',
'devtools:',
'https://clients2.google.com/service/update2/crx',
'https://clients2.googleusercontent.com/crx',
];
const log = getLogger('hardenSession');
/**
* Hardens a session to prevent loading resources outside the renderer resources and
* to reject all permission requests.
*
* In dev mode, installation of extensions and opening the devtools will be allowed.
*
* @param resources The resource handle associated with the paths and URL of the application.
* @param devMode Whether the application is in development mode.
* @param session The session to harden.
*/
export default function hardenSession(
resources: Resources,
devMode: boolean,
session: Session,
): void {
session.setPermissionRequestHandler((_webContents, _permission, callback) => {
callback(false);
});
const rendererBaseURL = resources.getRendererURL('/');
log.debug('Renderer base URL:', rendererBaseURL);
const allowedPrefixes = [rendererBaseURL];
if (devMode) {
const webSocketBaseURL = rendererBaseURL.replace(/^http(s)?:/, 'ws$1:');
log.debug('WebSocket base URL:', webSocketBaseURL);
allowedPrefixes.push(webSocketBaseURL, ...DEVMODE_ALLOWED_URL_PREFIXES);
}
function shouldCancelRequest(url: string, method: string): boolean {
if (method !== 'GET') {
return true;
}
let normalizedURL: string;
try {
normalizedURL = new URL(url).toString();
} catch {
return true;
}
return !allowedPrefixes.some((prefix) => normalizedURL.startsWith(prefix));
}
session.webRequest.onBeforeRequest(({ url, method }, callback) => {
const cancel = shouldCancelRequest(url, method);
if (cancel) {
log.error('Prevented loading', method, url);
}
callback({ cancel });
});
}
|