aboutsummaryrefslogtreecommitdiffstats
path: root/packages/main/src/infrastructure/electron/impl/__tests__/hardenSession.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/main/src/infrastructure/electron/impl/__tests__/hardenSession.test.ts')
-rw-r--r--packages/main/src/infrastructure/electron/impl/__tests__/hardenSession.test.ts163
1 files changed, 163 insertions, 0 deletions
diff --git a/packages/main/src/infrastructure/electron/impl/__tests__/hardenSession.test.ts b/packages/main/src/infrastructure/electron/impl/__tests__/hardenSession.test.ts
new file mode 100644
index 0000000..bbbd675
--- /dev/null
+++ b/packages/main/src/infrastructure/electron/impl/__tests__/hardenSession.test.ts
@@ -0,0 +1,163 @@
1/*
2 * Copyright (C) 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
21import { URL } from 'node:url';
22
23import { jest } from '@jest/globals';
24import { fake } from '@sophie/test-utils';
25import type {
26 OnBeforeRequestListenerDetails,
27 PermissionRequestHandlerHandlerDetails,
28 Response,
29 Session,
30 WebContents,
31} from 'electron';
32
33import type Resources from '../../../resources/Resources.js';
34import hardenSession from '../hardenSession.js';
35
36const permissions = [
37 'clipboard-read',
38 'media',
39 'display-capture',
40 'mediaKeySystem',
41 'geolocation',
42 'notifications',
43 'midi',
44 'midiSysex',
45 'pointerLock',
46 'fullscreen',
47 'openExternal',
48 'unknown',
49] as const;
50
51type Permission = typeof permissions[number];
52
53let permissionRequestHandler:
54 | ((
55 webContents: WebContents,
56 permission: Permission,
57 callback: (permissionGranted: boolean) => void,
58 details: PermissionRequestHandlerHandlerDetails,
59 ) => void)
60 | undefined;
61
62let onBeforeRequest:
63 | ((
64 details: OnBeforeRequestListenerDetails,
65 callback: (response: Response) => void,
66 ) => void)
67 | undefined;
68
69function getFakeResources(devMode: boolean) {
70 const resourcesUrl = devMode
71 ? 'http://localhost:3000/'
72 : 'file:///opt/sophie/resources/app.asar/renderer/dist/';
73 return fake<Resources>({
74 getRendererURL(path: string) {
75 return new URL(path, resourcesUrl).toString();
76 },
77 });
78}
79
80const fakeSession = fake<Session>({
81 setPermissionRequestHandler(handler) {
82 permissionRequestHandler =
83 handler instanceof Function ? handler : undefined;
84 },
85 webRequest: {
86 onBeforeRequest(handler) {
87 onBeforeRequest = handler instanceof Function ? handler : undefined;
88 },
89 },
90});
91
92beforeEach(() => {
93 permissionRequestHandler = undefined;
94 onBeforeRequest = undefined;
95});
96
97test('set permission request and before request handlers', () => {
98 hardenSession(getFakeResources(false), false, fakeSession);
99 expect(permissionRequestHandler).toBeDefined();
100 expect(onBeforeRequest).toBeDefined();
101});
102
103test.each(permissions.map((permission) => [permission]))(
104 'reject %s permission requests',
105 (permission: Permission) => {
106 hardenSession(getFakeResources(false), false, fakeSession);
107 const callback = jest.fn();
108 permissionRequestHandler!(fake<WebContents>({}), permission, callback, {
109 requestingUrl:
110 'file:///opt/sophie/resources/app.asar/pacakges/renderer/dist/index.html',
111 isMainFrame: true,
112 });
113 expect(callback).toHaveBeenCalledWith(false);
114 },
115);
116
117test.each([
118 [
119 false,
120 'GET',
121 'file:///opt/sophie/resources/app.asar/pacakges/renderer/dist/index.html',
122 false,
123 ],
124 [
125 false,
126 'POST',
127 'file:///opt/sophie/resources/app.asar/pacakges/renderer/dist/index.html',
128 true,
129 ],
130 [false, 'GET', 'chrome-extension:aaaa', true],
131 [false, 'GET', 'devtools:aaaa', true],
132 [false, 'GET', 'https://clients2.google.com/service/update2/crx/aaaa', true],
133 [false, 'GET', 'https://clients2.googleusercontent.com/crx/aaaa', true],
134 [false, 'GET', 'https://example.com', true],
135 [false, 'GET', 'invalid-url', true],
136 [true, 'GET', 'http://localhost:3000/index.html', false],
137 [true, 'POST', 'http://localhost:3000/index.html', true],
138 [true, 'GET', 'ws://localhost:3000/index.html', false],
139 [true, 'GET', 'chrome-extension:aaaa', false],
140 [true, 'GET', 'devtools:aaaa', false],
141 [true, 'GET', 'https://clients2.google.com/service/update2/crx/aaaa', false],
142 [true, 'GET', 'https://clients2.googleusercontent.com/crx/aaaa', false],
143 [true, 'GET', 'https://example.com', true],
144])(
145 'in dev mode: %s the request %s %s should be cancelled: %s',
146 (devMode: boolean, method: string, url: string, cancel: boolean) => {
147 hardenSession(getFakeResources(devMode), devMode, fakeSession);
148 const callback = jest.fn();
149 onBeforeRequest!(
150 {
151 id: 0,
152 url,
153 method,
154 resourceType: 'mainFrame',
155 referrer: '',
156 timestamp: 0,
157 uploadData: [],
158 },
159 callback,
160 );
161 expect(callback).toHaveBeenCalledWith(expect.objectContaining({ cancel }));
162 },
163);