aboutsummaryrefslogtreecommitdiffstats
path: root/packages/service-inject
diff options
context:
space:
mode:
Diffstat (limited to 'packages/service-inject')
-rw-r--r--packages/service-inject/src/index.ts8
-rw-r--r--packages/service-inject/src/shims/userAgentData.ts103
-rw-r--r--packages/service-inject/src/utils.ts104
3 files changed, 1 insertions, 214 deletions
diff --git a/packages/service-inject/src/index.ts b/packages/service-inject/src/index.ts
index f699f11..a7ada84 100644
--- a/packages/service-inject/src/index.ts
+++ b/packages/service-inject/src/index.ts
@@ -18,10 +18,4 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { shimUserAgentData } from './shims/userAgentData'; 21export {}
22
23try {
24 shimUserAgentData('96', 'Linux');
25} catch (err) {
26 console.log('Failed to execute injected script:', err);
27}
diff --git a/packages/service-inject/src/shims/userAgentData.ts b/packages/service-inject/src/shims/userAgentData.ts
deleted file mode 100644
index 7e2c825..0000000
--- a/packages/service-inject/src/shims/userAgentData.ts
+++ /dev/null
@@ -1,103 +0,0 @@
1/*
2 * Copyright (C) 2021-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 {
22 defineProtoProperty,
23 deleteProtoProperty,
24 simulateNativeClass,
25 simulateNativeFunction,
26} from '../utils';
27
28export function shimUserAgentData(chromeVersion: string | null, platform: string): void {
29 const brands = [
30 {
31 brand: ' Not A; Brand',
32 version: '99',
33 },
34 ];
35 if (chromeVersion !== null) {
36 brands.push({
37 brand: 'Chromium',
38 version: '96',
39 });
40 }
41 const mobile = false;
42
43 const simulatedNavigatorUa = simulateNativeClass('NavigatorUAData', function NavigatorUAData() {
44 // Nothing to initiailize.
45 }, {
46 brands: {
47 configurable: true,
48 enumerable: true,
49 get: simulateNativeFunction('brands', () => brands),
50 },
51 mobile: {
52 configurable: true,
53 enumerable: true,
54 get: simulateNativeFunction('mobile', () => mobile),
55 },
56 platform: {
57 configurable: true,
58 enumerable: true,
59 get: simulateNativeFunction('platform', () => platform),
60 },
61 getHighEntropyValues: {
62 configurable: true,
63 enumerable: false,
64 value: simulateNativeFunction('getHighEntropyValues', (...args: unknown[]) => {
65 if (args.length == 0) {
66 throw new TypeError("Failed to execute 'getHighEntropyValues' on 'NavigatorUAData': 1 argument required, but only 0 present.");
67 }
68 const hints = Array.from(args[0] as Iterable<string>);
69 if (hints.length === 0) {
70 return {};
71 }
72 const data: Record<string, unknown> = {
73 brands,
74 mobile,
75 }
76 if (hints.includes('platform')) {
77 data['platform'] = platform;
78 }
79 return Promise.resolve(data);
80 })
81 },
82 toJSON: {
83 configurable: true,
84 enumerable: false,
85 value: simulateNativeFunction('toJSON', () => ({
86 brands,
87 mobile,
88 })),
89 writable: false,
90 },
91 });
92
93 const simulatedUserAgentData = Reflect.construct(simulatedNavigatorUa, []);
94 defineProtoProperty(globalThis.navigator, 'userAgentData', {
95 configurable: true,
96 enumerable: true,
97 get: simulateNativeFunction('userAgentData', () => simulatedUserAgentData),
98 });
99}
100
101export function deleteUserAgentData(): void {
102 deleteProtoProperty(globalThis.navigator, 'userAgentData');
103}
diff --git a/packages/service-inject/src/utils.ts b/packages/service-inject/src/utils.ts
deleted file mode 100644
index 4bb3fba..0000000
--- a/packages/service-inject/src/utils.ts
+++ /dev/null
@@ -1,104 +0,0 @@
1/*
2 * Copyright (C) 2021-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/**
22 * Simulates a function defined in native code, i.e., one with
23 * `[native code]` in its `toString`.
24 *
25 * @param name The name of the function.
26 * @param f The function to transform.
27 * @return The transformed function.
28 */
29export function simulateNativeFunction<T, P extends unknown[]>(
30 name: string,
31 f: (this: null, ...args: P) => T,
32): (...args: P) => T {
33 // Bound functions say `[native code]`, but unfortunately they omit the function name:
34 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString#description
35 // The type of `f` contains type variables, so we need some magic type casting.
36 const boundFunc = f.bind(null as ThisParameterType<typeof f>);
37 Object.defineProperty(boundFunc, 'name', {
38 configurable: true,
39 enumerable: false,
40 value: name,
41 writable: false,
42 });
43 return boundFunc;
44}
45
46/**
47 * Simulates a native class available on `globalThis`.
48 *
49 * @param name The name of the class.
50 * @param constructor The constructor function. Must already be a constructor (a named `function`).
51 * @param properties The properties to define on the prototype.
52 */
53export function simulateNativeClass(
54 name: string,
55 constructor: () => void,
56 properties: PropertyDescriptorMap,
57) {
58 Object.defineProperties(constructor.prototype, {
59 [Symbol.toStringTag]: {
60 configurable: true,
61 enumerable: false,
62 value: name,
63 writable: false,
64 },
65 ...properties,
66 });
67 const simulatedConstructor = simulateNativeFunction(name, constructor);
68 Object.defineProperty(globalThis, name, {
69 configurable: true,
70 enumerable: true,
71 value: simulatedConstructor,
72 writable: true,
73 });
74 return simulatedConstructor;
75}
76
77/**
78 * Defines a property on the prototype of an object.
79 *
80 * Only use this with singleton objects, e.g., `window.navigator`.
81 *
82 * @param o The object to modify. Must be a singleton.
83 * @param property The key of the property being defined or modified.
84 * @param attributes The descriptor of the property being defined or modified.
85 */
86export function defineProtoProperty(
87 o: object,
88 property: PropertyKey,
89 attributes: PropertyDescriptor,
90): void {
91 Object.defineProperty(Object.getPrototypeOf(o), property, attributes);
92}
93
94/**
95 * Deletes a property from the prototype of an object.
96 *
97 * Only use this with singleton objects, e.g., `window.navigator`.
98 *
99 * @param o The object to modify. Must be a singleton.
100 * @param property The key of the property being deleted.
101 */
102export function deleteProtoProperty(o: object, property: PropertyKey): void {
103 Reflect.deleteProperty(Object.getPrototypeOf(o), property);
104}