Skip to content

Commit dd4d143

Browse files
petrpatekB4nan
andauthored
fix: added webdriver hidding to chromium based browsers (#74)
Co-authored-by: Martin Adámek <[email protected]>
1 parent 7d78d77 commit dd4d143

File tree

6 files changed

+71
-20
lines changed

6 files changed

+71
-20
lines changed

src/abstract-classes/browser-plugin.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,36 @@ export abstract class BrowserPlugin<
140140
async launch(
141141
launchContext: LaunchContext<Library, LibraryOptions, LaunchResult, NewPageOptions, NewPageResult> = this.createLaunchContext(),
142142
): Promise<LaunchResult> {
143-
const { proxyUrl } = launchContext;
143+
const { proxyUrl, launchOptions }: { proxyUrl?: string; launchOptions: Record<string, any> } = launchContext;
144144

145145
if (proxyUrl) {
146146
await this._addProxyToLaunchOptions(launchContext);
147147
}
148148

149+
if (this._isChromiumBasedBrowser(launchContext)) {
150+
// This will set the args for chromium based browsers to hide the webdriver.
151+
launchOptions.args = this._mergeArgsToHideWebdriver(launchOptions.args);
152+
}
153+
149154
return this._launch(launchContext);
150155
}
151156

157+
private _mergeArgsToHideWebdriver(originalArgs: string[]): string[] {
158+
if (!originalArgs?.length) {
159+
return ['--disable-blink-features=AutomationControlled'];
160+
}
161+
162+
const argumentIndex = originalArgs.findIndex((arg: string) => arg.startsWith('--disable-blink-features='));
163+
164+
if (argumentIndex !== -1) {
165+
originalArgs[argumentIndex] += ',AutomationControlled';
166+
} else {
167+
originalArgs.push('--disable-blink-features=AutomationControlled');
168+
}
169+
170+
return originalArgs;
171+
};
172+
152173
/**
153174
* @private
154175
*/
@@ -158,6 +179,12 @@ export abstract class BrowserPlugin<
158179
throwImplementationNeeded('_addProxyToLaunchOptions');
159180
}
160181

182+
// @ts-expect-error Give runtime error as well as compile time
183+
// eslint-disable-next-line space-before-function-paren, @typescript-eslint/no-unused-vars, max-len
184+
protected abstract _isChromiumBasedBrowser(launchContext: LaunchContext<Library, LibraryOptions, LaunchResult, NewPageOptions, NewPageResult>): boolean {
185+
throwImplementationNeeded('_isChromiumBasedBrowser');
186+
}
187+
161188
/**
162189
* @private
163190
*/

src/fingerprinting/hooks.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { FingerprintInjector } from 'fingerprint-injector';
22
import { BrowserPool, PlaywrightPlugin, PuppeteerPlugin } from '..';
33
import { BrowserController } from '../abstract-classes/browser-controller';
44
import { LaunchContext } from '../launch-context';
5-
import { getGeneratorDefaultOptions, mergeArgsToHideWebdriver } from './utils';
5+
import { getGeneratorDefaultOptions } from './utils';
66

77
export function createFingerprintPreLaunchHook(browserPool: BrowserPool<any, any, any, any, any>) {
88
const {
@@ -31,8 +31,6 @@ export function createFingerprintPreLaunchHook(browserPool: BrowserPool<any, any
3131
}
3232

3333
launchContext.extend({ fingerprint });
34-
// hide webdriver with arg.
35-
launchOptions.args = mergeArgsToHideWebdriver(launchOptions.args);
3634

3735
if (useIncognitoPages) {
3836
return;

src/fingerprinting/utils.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,6 @@ export const getGeneratorDefaultOptions = (launchContext: LaunchContext): Finger
1616
return options;
1717
};
1818

19-
export const mergeArgsToHideWebdriver = (originalArgs: string[]): string[] => {
20-
if (!originalArgs?.length) {
21-
return ['--disable-blink-features=AutomationControlled'];
22-
}
23-
24-
const argumentIndex = originalArgs.findIndex((arg: string) => arg.startsWith('--disable-blink-features='));
25-
26-
if (argumentIndex !== -1) {
27-
originalArgs[argumentIndex] += ',AutomationControlled';
28-
} else {
29-
originalArgs.push('--disable-blink-features=AutomationControlled');
30-
}
31-
32-
return originalArgs;
33-
};
34-
3519
const getBrowserName = (browserPlugin: BrowserPlugin, launchOptions: any): BrowserName => {
3620
const { library } = browserPlugin;
3721
let browserName;

src/playwright/playwright-plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,9 @@ export class PlaywrightPlugin extends BrowserPlugin<BrowserType, Parameters<Brow
9292
};
9393
}
9494
}
95+
96+
protected _isChromiumBasedBrowser(): boolean {
97+
const name = this.library.name();
98+
return name === 'chromium';
99+
}
95100
}

src/puppeteer/puppeteer-plugin.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,11 @@ export class PuppeteerPlugin extends BrowserPlugin<typeof Puppeteer> {
163163
}
164164
*/
165165
}
166+
167+
protected _isChromiumBasedBrowser(launchContext: LaunchContext<any, unknown, any, unknown, any>): boolean {
168+
const { launchOptions } = launchContext as any;
169+
// @ts-expect-error cannot find .product on this.library
170+
const browserName = launchOptions.product || this.library.product!;
171+
return browserName === 'chrome';
172+
}
166173
}

test/browser-pool.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,36 @@ describe('BrowserPool', () => {
637637
});
638638
});
639639

640+
describe('default browser automation masking', () => {
641+
describe.each(fingerprintingMatrix)('%s', (_name, plugin) => {
642+
let browserPoolWithDefaults: BrowserPool;
643+
let page: any;
644+
645+
beforeEach(async () => {
646+
browserPoolWithDefaults = new BrowserPool({
647+
browserPlugins: [plugin],
648+
closeInactiveBrowserAfterSecs: 2,
649+
});
650+
page = await browserPoolWithDefaults.newPage();
651+
});
652+
653+
afterEach(async () => {
654+
if (page) await page.close();
655+
656+
await browserPoolWithDefaults.destroy();
657+
});
658+
659+
test('should hide webdriver', async () => {
660+
await page.goto(`file://${__dirname}/test.html`);
661+
const webdriver = await page.evaluate(() => {
662+
return navigator.webdriver;
663+
});
664+
// Can be undefined or false, depending on the chrome version.
665+
expect(webdriver).toBeFalsy();
666+
});
667+
});
668+
});
669+
640670
describe('fingerprinting', () => {
641671
describe.each(fingerprintingMatrix)('%s', (_name, plugin) => {
642672
let browserPoolWithFP: BrowserPool;

0 commit comments

Comments
 (0)