fix(stealth): neutralize disable-devtool auto bootstrap
This commit is contained in:
@@ -126,6 +126,34 @@ describe('Stealth mode', () => {
|
||||
expect(signals.inNavigator).toBe(false);
|
||||
});
|
||||
|
||||
it('hides disable-devtool auto bootstrap selector while keeping normal selectors intact', async () => {
|
||||
browser = new BrowserManager();
|
||||
await browser.launch({ headless: true, stealth: true });
|
||||
|
||||
const signals = await browser.getPage().evaluate(() => {
|
||||
const marker = document.createElement('meta');
|
||||
marker.setAttribute('data-ab-marker', 'ok');
|
||||
document.head.appendChild(marker);
|
||||
|
||||
const dd = document.createElement('script');
|
||||
dd.setAttribute('disable-devtool-auto', '');
|
||||
document.head.appendChild(dd);
|
||||
|
||||
const hiddenOne = document.querySelector('[disable-devtool-auto]') === null;
|
||||
const hiddenCount = document.querySelectorAll('[disable-devtool-auto]').length;
|
||||
const normalSelectorWorks = !!document.querySelector('[data-ab-marker="ok"]');
|
||||
|
||||
dd.remove();
|
||||
marker.remove();
|
||||
|
||||
return { hiddenOne, hiddenCount, normalSelectorWorks };
|
||||
});
|
||||
|
||||
expect(signals.hiddenOne).toBe(true);
|
||||
expect(signals.hiddenCount).toBe(0);
|
||||
expect(signals.normalSelectorWorks).toBe(true);
|
||||
});
|
||||
|
||||
it('neutralizes creepjs prefers-color-scheme light probe', async () => {
|
||||
browser = new BrowserManager();
|
||||
await browser.launch({ headless: true, stealth: true });
|
||||
|
||||
@@ -292,6 +292,7 @@ function buildStealthScript(options: StealthScriptOptions): string {
|
||||
configScript,
|
||||
patchNavigatorWebdriver(),
|
||||
patchCssSupportsWebdriverHeuristic(),
|
||||
patchDisableDevtoolAutoBootstrap(),
|
||||
patchChromeRuntime(),
|
||||
patchChromeLegacyApis(),
|
||||
patchIframeContentWindow(),
|
||||
@@ -366,6 +367,65 @@ function patchCssSupportsWebdriverHeuristic(): string {
|
||||
})();`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some high-risk sites ship disable-devtool with auto bootstrap via
|
||||
* document.querySelector('[disable-devtool-auto]').
|
||||
*
|
||||
* Hiding only this selector prevents the default self-close/redirect behavior
|
||||
* without affecting normal selector queries.
|
||||
*/
|
||||
function patchDisableDevtoolAutoBootstrap(): string {
|
||||
return `(function(){
|
||||
if (typeof Document === 'undefined') return;
|
||||
const AUTO_SELECTOR = '[disable-devtool-auto]';
|
||||
const NEVER_MATCH_SELECTOR = 'script[__ab_disable_devtool_never_match__="1"]';
|
||||
const normalize = (value) => {
|
||||
if (typeof value !== 'string') return '';
|
||||
return value.replace(/\\s+/g, '').toLowerCase();
|
||||
};
|
||||
const shouldHideSelector = (selector) => normalize(selector) === AUTO_SELECTOR;
|
||||
|
||||
const patchQueryMethod = (proto, method) => {
|
||||
if (!proto) return;
|
||||
const native = proto[method];
|
||||
if (typeof native !== 'function') return;
|
||||
|
||||
const wrapped = function(selector, ...args) {
|
||||
if (shouldHideSelector(selector)) {
|
||||
return native.call(this, NEVER_MATCH_SELECTOR, ...args);
|
||||
}
|
||||
return native.call(this, selector, ...args);
|
||||
};
|
||||
|
||||
try {
|
||||
Object.defineProperty(wrapped, 'name', {
|
||||
value: native.name,
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(wrapped, 'toString', {
|
||||
value: () => native.toString(),
|
||||
configurable: true,
|
||||
});
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
Object.defineProperty(proto, method, {
|
||||
value: wrapped,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
} catch {}
|
||||
};
|
||||
|
||||
patchQueryMethod(Document.prototype, 'querySelector');
|
||||
patchQueryMethod(Document.prototype, 'querySelectorAll');
|
||||
if (typeof Element !== 'undefined') {
|
||||
patchQueryMethod(Element.prototype, 'querySelector');
|
||||
patchQueryMethod(Element.prototype, 'querySelectorAll');
|
||||
}
|
||||
})();`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove navigator.webdriver entirely.
|
||||
* Modern detection checks both value and property presence (`'webdriver' in navigator`).
|
||||
|
||||
Reference in New Issue
Block a user