Skip to content

A Bot's browser announces itself as HeadlessChrome with webdriver true, so Google refuses sign-in even when a person takes the wheel #275

Description

@jerelvelarde

A Bot's browser identifies itself as headless and automated, so Google refuses sign-in with "Couldn't sign you in — This browser or app may not be secure." Take-the-wheel exists so a person can finish exactly this kind of sign-in, and the person cannot: the refusal is on Google's side and no amount of clicking gets past it.

This is not a heuristic that happened to fire. The browser is announcing it.

What is wrong

LAUNCH_ARGS (agent-computer/src/profiles.ts:95-99) is three flags, and nothing anywhere in agent-computer/src touches automation signalling — no --disable-blink-features=AutomationControlled, no enable-automation exclusion, no user-agent override, and no channel on chromium.launchPersistentContext (profiles.ts:296), so the launch is Playwright's bundled Chromium at its default, which is headless.

Measured from inside a running Bot's browser on this deployment, by navigating it to a page that prints what a site sees:

{
 "userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/151.0.7922.34 Safari/537.36",
 "webdriver": true,
 "headlessHint": true,
 "uaDataBrands": null,
 "plugins": 0,
 "languages": ["en-US@posix"]
}

HeadlessChrome in the user-agent string and navigator.webdriver === true are each independently sufficient for Google's sign-in to refuse. navigator.userAgentData.brands being null and zero plugins are consistent with the same. The en-US@posix locale is a separate oddity worth knowing about — it is a POSIX locale modifier rather than a language tag, and sites that parse navigator.languages may mishandle it.

Why it matters

It removes the feature rather than degrading it. A Bot that meets a Google sign-in cannot proceed, and neither can the person who takes the wheel to help it, so the escape hatch and the thing it is an escape hatch from fail together. Google sign-in is a common gate in front of ordinary work, and Google is not the only provider that checks.

It is also invisible until late. Nothing at start-up says the browser will be refused by some sites; the operator finds out when a person is already sitting in front of a takeover that cannot be completed.

Related: #270 blocks the same workflow at a different point (a popup sign-in window is unreachable). Fixing either one alone still leaves popup-based Google sign-in broken.

On what a fix should and should not be

Worth stating plainly, because the obvious fix is the wrong one: the goal here is not to defeat bot detection. Google's check is correct — it is an automated browser. Suppressing the signals while the Bot drives would make the detector wrong rather than the situation better, and may run against the provider's terms.

What is defensible is narrower: when a person has taken the wheel and is signing into their own account, the browser they are typing into should be an ordinary browser, because at that moment the automation claim is no longer true. Options, with their costs:

  • Run headed under Xvfb. Removes HeadlessChrome from the user-agent honestly, because the browser genuinely is not headless. Costs an X server in the image and some memory.
  • Use a real Chrome build (channel: "chrome") rather than bundled Chromium. Changes what ships in the image and its licensing.
  • Leave navigator.webdriver alone. It is true, and a deployment that lies about it is claiming something false about itself.
  • Document the limitation and point at the credential vault for providers that cannot be signed into interactively — the honest answer if the above are judged not worth it.

Whatever is chosen, saying it at start-up alongside the existing computer-sandbox line would stop an operator discovering it mid-takeover.

How to check it is fixed

Take the wheel on a page that offers Google sign-in and complete it. As a cheaper proxy, navigate a Bot to a page printing navigator.userAgent and navigator.webdriver and confirm the user-agent no longer contains Headless.

Scope of verification

The fingerprint above was measured on a real deployment at 7fda66a plus the branch for #270, through agent-computer's own /navigate and /read. The Google refusal was observed by a person holding the wheel. I have not tested any of the suggested remedies.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions