Skip to content

Commit 561cb93

Browse files
shakyShaneShane Osbourne
and
Shane Osbourne
authored
fix: remove the conditional that prevented some permissions being overridden on windows (#529)
* fix: remove the conditional that prevented some permissions being overridden on windows https://app.asana.com/0/0/1204646741037461/f * addInitScript > evaluate --------- Co-authored-by: Shane Osbourne <[email protected]>
1 parent d58dbff commit 561cb93

File tree

4 files changed

+158
-1
lines changed

4 files changed

+158
-1
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { test, expect } from '@playwright/test'
2+
import { readFileSync } from 'fs'
3+
import { mockWindowsMessaging, wrapWindowsScripts } from '@duckduckgo/messaging/lib/test-utils.mjs'
4+
5+
test('Windows Permissions Usage', async ({ page }) => {
6+
const perms = new WindowsPermissionsSpec(page)
7+
await perms.enabled()
8+
const results = await page.evaluate(() => {
9+
// @ts-expect-error - this is added by the test framework
10+
return window.results['Disabled Windows Permissions']
11+
})
12+
13+
for (const result of results) {
14+
expect(result.result).toEqual(result.expected)
15+
}
16+
})
17+
18+
export class WindowsPermissionsSpec {
19+
htmlPage = '/permissions/index.html'
20+
config = './integration-test/test-pages/permissions/config/permissions.json'
21+
build = './build/windows/contentScope.js'
22+
/**
23+
* @param {import("@playwright/test").Page} page
24+
*/
25+
constructor (page) {
26+
this.page = page
27+
}
28+
29+
async enabled () {
30+
await this.installPolyfills()
31+
const config = JSON.parse(readFileSync(this.config, 'utf8'))
32+
await this.setup({ config })
33+
await this.page.goto(this.htmlPage)
34+
}
35+
36+
/**
37+
* In CI, the global objects such as USB might not be installed on the
38+
* version of chromium running there.
39+
*/
40+
async installPolyfills () {
41+
await this.page.addInitScript(() => {
42+
// @ts-expect-error - testing
43+
if (typeof Bluetooth === 'undefined') {
44+
globalThis.Bluetooth = {}
45+
globalThis.Bluetooth.prototype = { requestDevice: async () => { /* noop */ } }
46+
}
47+
// @ts-expect-error - testing
48+
if (typeof USB === 'undefined') {
49+
globalThis.USB = {}
50+
globalThis.USB.prototype = { requestDevice: async () => { /* noop */ } }
51+
}
52+
53+
// @ts-expect-error - testing
54+
if (typeof Serial === 'undefined') {
55+
globalThis.Serial = {}
56+
globalThis.Serial.prototype = { requestPort: async () => { /* noop */ } }
57+
}
58+
// @ts-expect-error - testing
59+
if (typeof HID === 'undefined') {
60+
globalThis.HID = {}
61+
globalThis.HID.prototype = { requestDevice: async () => { /* noop */ } }
62+
}
63+
})
64+
}
65+
66+
/**
67+
* @param {object} params
68+
* @param {Record<string, any>} params.config
69+
* @return {Promise<void>}
70+
*/
71+
async setup (params) {
72+
const { config } = params
73+
74+
// read the built file from disk and do replacements
75+
const injectedJS = wrapWindowsScripts(this.buildArtefact, {
76+
$CONTENT_SCOPE$: config,
77+
$USER_UNPROTECTED_DOMAINS$: [],
78+
$USER_PREFERENCES$: {
79+
platform: { name: 'windows' },
80+
debug: true
81+
}
82+
})
83+
84+
await this.page.addInitScript(mockWindowsMessaging, {
85+
messagingContext: {
86+
env: 'development',
87+
context: 'contentScopeScripts',
88+
featureName: 'n/a'
89+
},
90+
responses: {}
91+
})
92+
93+
// attach the JS
94+
await this.page.addInitScript(injectedJS)
95+
}
96+
97+
/**
98+
* @return {string}
99+
*/
100+
get buildArtefact () {
101+
const buildArtefact = readFileSync(this.build, 'utf8')
102+
return buildArtefact
103+
}
104+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"unprotectedTemporary": [],
3+
"features": {
4+
"windowsPermissionUsage": {
5+
"state": "enabled",
6+
"exceptions": []
7+
}
8+
}
9+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width">
6+
<title>Permissions</title>
7+
<link rel="stylesheet" href="../shared/style.css">
8+
</head>
9+
<body>
10+
<script src="../shared/utils.js"></script>
11+
12+
<p>Windows permission usage</p>
13+
14+
<script>
15+
test('Disabled Windows Permissions', async () => {
16+
const registerProtocolHandler = await captureError(() => Navigator.prototype.registerProtocolHandler())
17+
const requestDevice = await captureError(() => Bluetooth.prototype.requestDevice())
18+
const usbDevice = await captureError(() => USB.prototype.requestDevice())
19+
const serialPort = await captureError(() => Serial.prototype.requestPort())
20+
const hidDevice = await captureError(() => HID.prototype.requestDevice())
21+
const midi = await captureError(() => Navigator.prototype.requestMIDIAccess())
22+
23+
return [
24+
{ name: 'Bluetooth.prototype.requestDevice()', result: requestDevice.toString(), expected: 'Error: Permission denied' },
25+
{ name: 'USB.prototype.requestDevice', result: usbDevice.toString(), expected: 'Error: Permission denied' },
26+
{ name: 'Serial.prototype.requestPort', result: serialPort.toString(), expected: 'Error: Permission denied' },
27+
{ name: 'HID.prototype.requestDevice', result: hidDevice.toString(), expected: 'Error: Permission denied' },
28+
{ name: 'Protocol handler: Navigator.prototype.registerProtocolHandler', result: registerProtocolHandler.toString(), expected: 'Error: Permission denied' },
29+
{ name: 'MIDI: Navigator.prototype.requestMIDIAccess', result: midi.toString(), expected: 'Error: Permission denied' },
30+
];
31+
})
32+
33+
async function captureError(fn) {
34+
try {
35+
// ensure Promise.reject is captured
36+
return fn().catch(e => e)
37+
} catch (e) {
38+
return e
39+
}
40+
}
41+
42+
renderResults();
43+
</script>
44+
</body>
45+
</html>

src/features/windows-permission-usage.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,6 @@ export default class WindowsPermissionUsage extends ContentFeature {
393393
]
394394
for (const { name, prototype, method, isPromise } of permissionsToDisable) {
395395
try {
396-
if (typeof window[name] === 'undefined') continue
397396
const proxy = new DDGProxy(featureName, prototype(), method, {
398397
apply () {
399398
if (isPromise) {

0 commit comments

Comments
 (0)