Change navigator.webdriver with Playwright

2k Views Asked by At

I'm using Playwright with Chrome.

In other tools, one can easily change navigator.webdriver like so:

Object.defineProperty(navigator, 'webdriver', { get: () => false });

In Playwright + Chrome (using .NET bindings, but same idea in Node / Python), I tried:

// attempt 1
await context.AddInitScriptAsync("""
  Object.defineProperty(navigator, 'webdriver', { get: () => false });
""");

// attempt 2
await page.EvaluateAsync("""
  Object.defineProperty(navigator, 'webdriver', { get: () => false });
""");

// attempt 3
await page.EvaluateAsync("""
  delete Object.getPrototypeOf(navigator).webdriver;
  Object.defineProperty(navigator, 'webdriver', { get: () => false });
""");

// attempt 4
await page.EvaluateAsync("""
  const newProto = navigator.__proto__;
  delete newProto.webdriver;
  navigator.__proto__ = newProto;
""");

In all cases the browser still reports that it is using webdriver (e.g. here).

How do I do this with Playwright?

2

There are 2 best solutions below

6
Charchit Agarwal On

For patching properties in playwright, consider using page.add_init_script instead (or it's equivalent in other languages, this is for python). This is because the script you add through this method will automatically be called every time the page is navigated or whenever a child frame is attached, in their respective contexts. This will save you the hassle of doing it manually.

Example:

await page.add_init_script("delete Object.getPrototypeOf(navigator).webdriver")

The reason page.evaluate seemingly does not work has to do with the fact that it is run after the page has navigated (calling the method before that does not make sense because the changes will be overwritten after navigation anyway). Since the webpage cannot react to changes you do after it has already completed its tests, it makes it seem that page.evaluate is not working, which is not true.

Update:

Here's a more robust patch, courtesy of playwright-stealth:

s = """
if (navigator.webdriver === false) {
    // Post Chrome 89.0.4339.0 and already good
} else if (navigator.webdriver === undefined) {
    // Pre Chrome 89.0.4339.0 and already good
} else {
    // Pre Chrome 88.0.4291.0 and needs patching
    delete Object.getPrototypeOf(navigator).webdriver
}
"""

await page.add_init_script(s)    
4
George Y. On

A radical but ultimate method is to hex-edit chrome binary (in ~/.cache/ms-playwright/chromium-<whatever> and replace the webdriver string there with some other nine characters, i.e. aaaaaaaaa.