(hw: HostProfile)
| 80 | * defineProperty getters are covered by its native-code lie. |
| 81 | */ |
| 82 | export function buildStealthScript(hw: HostProfile): string { |
| 83 | return `(() => { |
| 84 | // ──── Function.prototype.toString Proxy (must run first) ──── |
| 85 | // Make every patched getter / function below report |
| 86 | // 'function NAME() { [native code] }' at every recursion depth. |
| 87 | // Defeats fn.toString.toString.toString() integrity checks. |
| 88 | const patchedFns = new WeakSet(); |
| 89 | const nativeToString = Function.prototype.toString; |
| 90 | const toStringProxy = new Proxy(nativeToString, { |
| 91 | apply(target, thisArg, args) { |
| 92 | if (patchedFns.has(thisArg)) { |
| 93 | const name = (thisArg && thisArg.name) || ''; |
| 94 | return 'function ' + name + '() { [native code] }'; |
| 95 | } |
| 96 | return Reflect.apply(target, thisArg, args); |
| 97 | }, |
| 98 | }); |
| 99 | Object.defineProperty(Function.prototype, 'toString', { |
| 100 | value: toStringProxy, writable: true, configurable: true, |
| 101 | }); |
| 102 | const markNative = (fn, name) => { |
| 103 | if (name) { |
| 104 | try { Object.defineProperty(fn, 'name', { value: name }); } catch {} |
| 105 | } |
| 106 | patchedFns.add(fn); |
| 107 | return fn; |
| 108 | }; |
| 109 | |
| 110 | // ──── navigator.webdriver (canonical mask, kept from D7) ──── |
| 111 | try { |
| 112 | const webdriverGetter = markNative(function() { return false; }, 'get webdriver'); |
| 113 | Object.defineProperty(navigator, 'webdriver', { get: webdriverGetter, configurable: true }); |
| 114 | } catch {} |
| 115 | |
| 116 | // ──── window.chrome.* restoration ──── |
| 117 | // Real Chrome ships these objects with rich enum / method shape. |
| 118 | // Headless Chromium / Playwright's launch strips them. Their absence |
| 119 | // is a universally-checked tell (verified in Cloudflare + DataDome |
| 120 | // RE catalogs). We don't try to perfectly mimic — we ship plausible |
| 121 | // shape with native-code-looking methods. |
| 122 | try { |
| 123 | if (!('chrome' in window)) { |
| 124 | window.chrome = {}; |
| 125 | } |
| 126 | const chrome = window.chrome; |
| 127 | if (!chrome.runtime) { |
| 128 | chrome.runtime = { |
| 129 | OnInstalledReason: { CHROME_UPDATE: 'chrome_update', INSTALL: 'install', |
| 130 | SHARED_MODULE_UPDATE: 'shared_module_update', UPDATE: 'update' }, |
| 131 | OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' }, |
| 132 | PlatformArch: { ARM: 'arm', ARM64: 'arm64', MIPS: 'mips', MIPS64: 'mips64', |
| 133 | X86_32: 'x86-32', X86_64: 'x86-64' }, |
| 134 | PlatformNaclArch: { ARM: 'arm', MIPS: 'mips', MIPS64: 'mips64', |
| 135 | X86_32: 'x86-32', X86_64: 'x86-64' }, |
| 136 | PlatformOs: { ANDROID: 'android', CROS: 'cros', LINUX: 'linux', |
| 137 | MAC: 'mac', OPENBSD: 'openbsd', WIN: 'win' }, |
| 138 | RequestUpdateCheckStatus: { NO_UPDATE: 'no_update', THROTTLED: 'throttled', |
| 139 | UPDATE_AVAILABLE: 'update_available' }, |
no outgoing calls
no test coverage detected