diff --git a/cli/src/native/stealth.rs b/cli/src/native/stealth.rs index 504213b..c85855f 100644 --- a/cli/src/native/stealth.rs +++ b/cli/src/native/stealth.rs @@ -15,23 +15,35 @@ const STEALTH_SCRIPTS_RAW: &str = include_str!("stealth_scripts.js"); /// Only removes navigator.webdriver — the browser's own fingerprint is already real. const MINIMAL_STEALTH_SCRIPT: &str = r#" (function(){ - // CDP sets a getter on Navigator.prototype.webdriver that returns true. - // Simple `delete` won't remove it. We must redefine the property with - // Object.defineProperty to fully hide it from `'webdriver' in navigator`. - const targets = [Navigator.prototype]; - if (typeof WorkerNavigator !== 'undefined') targets.push(WorkerNavigator.prototype); - targets.push(Object.getPrototypeOf(navigator)); - for (const target of targets) { - if (!target) continue; - try { delete target.webdriver; } catch {} - try { - Object.defineProperty(target, 'webdriver', { - get: undefined, + // CDP sets navigator.webdriver = true via a getter on Navigator.prototype. + // CreepJS detects THREE things: + // 1. navigator.webdriver === undefined (deletion = suspicious) + // 2. !!navigator.webdriver (true = automation) + // 3. lieProps (defineProperty tampering = suspicious) + // + // The correct fix: set it to FALSE using the native property descriptor + // pattern, not delete it or use defineProperty tricks. + // Normal Chrome has: Navigator.prototype.webdriver as a native getter returning false. + // CDP overrides it to return true. We override the value back to false. + try { + const proto = Navigator.prototype; + const desc = Object.getOwnPropertyDescriptor(proto, 'webdriver'); + if (desc && desc.get) { + // CDP sets a getter that returns true. Replace it with a getter + // that returns false — preserving the getter/setter shape so lie + // detection sees the same descriptor structure as a normal browser. + const nativeToString = desc.get.toString(); + const fakeGet = function webdriver() { return false; }; + // Match the native toString to avoid toString-based lie detection + fakeGet.toString = () => nativeToString; + Object.defineProperty(proto, 'webdriver', { + get: fakeGet, + set: undefined, configurable: true, + enumerable: true, }); - delete target.webdriver; - } catch {} - } + } + } catch {} })(); "#;