1

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?

lonix
  • 14,255
  • 23
  • 85
  • 176

2 Answers2

3

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)    
Charchit Agarwal
  • 2,829
  • 2
  • 8
  • 20
  • Thanks. I did try it (also I added it to the question above). Unfortunately it also doesn't work - when I call `navigator.webdriver` in the console then it correctly reports `false`, but the the detection [tests](https://bot.sannysoft.com/) always fail. – lonix Mar 17 '23 at 23:05
  • @lonix you are setting the webdriver property to false instead of deleting it, that is not always going to be a consistent patch. This is not about playwright and how it interacts with the browser environment, it's about how websites detect playwright and how those methods differ between browser versions. Hopefully the update above would be more helpful . – Charchit Agarwal Mar 18 '23 at 00:18
  • Thanks once again. Sadly I tried that too, and it's also detectable. I think the [problem](https://github.com/microsoft/playwright-python/issues/527#issuecomment-785155678) is that playwright/chromium itself does not expose this functionality. – lonix Mar 18 '23 at 00:46
  • @lonix I see, one strange thing I found was calling the method on the context (like in your code) fails the test, but calling it directly on the page (like in mine) passes the test on the website. Perhaps give that a go? It seems to work for me (I am using headful mode). Also you probably already are but make sure you are calling `add_init_script` before navigation. – Charchit Agarwal Mar 18 '23 at 01:00
  • Yeah I found all sorts of weird results like that. Also when running headful it sets/unsets the underlying chromium flags appropriately, but in headless it locks down everything and there's nothing to be done. I think all the code above probably worked a few versions ago, but no longer does. (Or does work, but is not consistent.) – lonix Mar 18 '23 at 01:03
  • method of playwright-stealth works for me. thanks – X.C. May 03 '23 at 08:22
1

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.

George Y.
  • 11,307
  • 3
  • 24
  • 25
  • +1 This sounds crazy until you realise that 1) if it's important then it's worth doing, 2) it can be scripted, and 3) it modifies the chrome dependency rather than the actual browser used by the desktop user (so it's not a big deal). Are you aware of any tools or methods to do this? – lonix Aug 25 '23 at 07:22
  • Of course if one is tempted to go down this route then a "cleaner" option is to compile a custom chromium from source. But it may be simpler to edit the binary as it is already part of the playwright toolchain and (other than that one edit) there would be less busywork. – lonix Aug 25 '23 at 07:25