aboutsummaryrefslogtreecommitdiffstats
path: root/packages/service-inject
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-25 00:01:18 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-25 00:01:18 +0100
commite321534fbea9f09b139d440584f6b84ad0afb80f (patch)
treec4b4df109589475dd5f40a0d31f47a6aa9e43195 /packages/service-inject
parentfeat: Shim userAgentData in all frames and workers (diff)
downloadsophie-e321534fbea9f09b139d440584f6b84ad0afb80f.tar.gz
sophie-e321534fbea9f09b139d440584f6b84ad0afb80f.tar.zst
sophie-e321534fbea9f09b139d440584f6b84ad0afb80f.zip
refactor: Simplify script injection
Inject CSS and main world scripts synchronously to avoid race conditions with page loading. Don't try to miming userAgentData for now, since it won't bypass google's checks. However, simply omitting chrome from the user agent does bypass them, at least for now.
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}