diff options
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.ts | 163 |
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..7b70d10 --- /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 | |||
21 | import { URL } from 'node:url'; | ||
22 | |||
23 | import { jest } from '@jest/globals'; | ||
24 | import { fake } from '@sophie/test-utils'; | ||
25 | import type { | ||
26 | OnBeforeRequestListenerDetails, | ||
27 | PermissionRequestHandlerHandlerDetails, | ||
28 | Response, | ||
29 | Session, | ||
30 | WebContents, | ||
31 | } from 'electron'; | ||
32 | |||
33 | import type Resources from '../../../resources/Resources'; | ||
34 | import hardenSession from '../hardenSession'; | ||
35 | |||
36 | const 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 | |||
51 | type Permission = typeof permissions[number]; | ||
52 | |||
53 | let permissionRequestHandler: | ||
54 | | (( | ||
55 | webContents: WebContents, | ||
56 | permission: Permission, | ||
57 | callback: (permissionGranted: boolean) => void, | ||
58 | details: PermissionRequestHandlerHandlerDetails, | ||
59 | ) => void) | ||
60 | | undefined; | ||
61 | |||
62 | let onBeforeRequest: | ||
63 | | (( | ||
64 | details: OnBeforeRequestListenerDetails, | ||
65 | callback: (response: Response) => void, | ||
66 | ) => void) | ||
67 | | undefined; | ||
68 | |||
69 | function 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 | |||
80 | const 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 | |||
92 | beforeEach(() => { | ||
93 | permissionRequestHandler = undefined; | ||
94 | onBeforeRequest = undefined; | ||
95 | }); | ||
96 | |||
97 | test('set permission request and before request handlers', () => { | ||
98 | hardenSession(getFakeResources(false), false, fakeSession); | ||
99 | expect(permissionRequestHandler).toBeDefined(); | ||
100 | expect(onBeforeRequest).toBeDefined(); | ||
101 | }); | ||
102 | |||
103 | test.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 | |||
117 | test.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 | ); | ||