console fill: never copy Google session cookies by default (device-bound); own profile in a Chrome started without automation switches

Google binds its session cookies to the device (Device Bound Session Credentials) and signs out
sessions whose cookies look stolen, which can sign the owner out of their real Chrome. The default is
now storebound's own profile per store; storebound starts Chrome itself (--user-data-dir,
--remote-debugging-port=0, no --enable-automation, navigator.webdriver false) and attaches over CDP.
--use-chrome-session google|apple is an explicit opt-in that warns. /signin/rejected is said plainly.
This commit is contained in:
CRTkafa 2026-10-12 07:46:04 +08:00
parent e0bef82b06
commit 0035931734
22 changed files with 456 additions and 156 deletions

View file

@ -199,11 +199,12 @@ write, never an MCP tool):
run lasts until the owner closes the browser: no timeout in serve, the runner or the host. Progress lines show in
the panel; **Cancel: close the browser** sends `$/cancelRequest`, which stops the command and its browser.
The browser is the same as in the terminal (cli/console/session.ts): a Chrome running with remote debugging, else on
macOS the owner's Chrome sign-in (real Google Chrome on a temporary copy of only `Local State` and the console cookies,
deleted when the run ends or is cancelled; the native dialog says "It uses your Chrome sign-in"), else storebound's own
profile in the desktop's Chrome for Testing (`STOREBOUND_CHROME`) with the bundled playwright-core. The tool's
`fresh_profile: true` passes `--fresh-profile`. storebound opens the form itself after sign-in (navigation clicks only).
The browser is the same as in the terminal (cli/console/session.ts): a Chrome running with remote debugging, else
storebound's own profile (the owner signs in there once; the native dialog says so) in the desktop's Chrome for
Testing (`STOREBOUND_CHROME`), started by storebound without automation switches and attached with the bundled
playwright-core. The desktop never copies the owner's Chrome cookies (Google binds them to the device; see the
console-autofill skill); `--use-chrome-session` is a terminal-only opt-in. The tool's `fresh_profile: true` passes
`--fresh-profile` (skip attach). storebound opens the form itself after sign-in (navigation clicks only).
The own profile is one per store (every studio shares the one login) and keeps the sign-in between runs: launched with
session restore, closed gracefully on Cancel. With one login in several Play developer accounts or App Store Connect
teams, the run finds the account that has the app and remembers it in `~/.storebound/console-accounts.json`

View file

@ -9,6 +9,7 @@ fails without the fix.
| # | date | lesson | guard |
|---|---|---|---|
| 18 | 2026-10 | `console fill play-app-record` with a copy of the owner's Chrome cookies reached the Play account chooser once, then Google answered `/v3/signin/rejected` and the chooser: Google session cookies are device-bound (Chrome's Device Bound Session Credentials) and Google signs out sessions whose cookies look stolen, which can also sign the owner out of their real Chrome. | Never copy Google cookies by default: storebound's own profile is the default (the owner signs in once by hand), started by storebound as a normal Chrome (no `--enable-automation`, `navigator.webdriver` false) and attached over CDP; `--use-chrome-session google\|apple` is an explicit opt-in that warns; `/signin/rejected` is said plainly (`cli/console/session.ts`, `tests/console-session.test.ts`). |
| 17 | 2026-10 | After the data types were saved, the real App Privacy page shows each type's "Set Up <type>" as a plain `<p>` in a div with a click handler (no button role, no link) and puts the types in a box after the Data Types heading's box: role-based locators and the "nearest heading" check found nothing, so no type was set up. A "?" (More information) button sits next to each type. | `setUpAllowed` / `setUpTexts` (`cli/lib/console-forms.ts`: exact text "Set Up <type>" of a type storebound answers, in the Data Types section, outside dialogs) and `setUpClick` / `setUpType` / `flowWhere` (last heading before the control) in `cli/console/fill.ts`; the per-type dialog steps are `page` sections, so an unknown step dumps and pauses instead of ending the run; `tests/fixtures/console/apple-app-privacy-setup.html` (structure of the real page) and `tests/console-flow.test.ts`. |
| 16 | 2026-10 | `console fill play-app-record` with the copied Chrome cookies printed only "still waiting for the sign-in" for 5+ minutes on a signed-in Play Console: Google pages embed `accounts.google.com` frames (the account widget, cookie rotation), and the detector counted any frame on a sign-in host. | `signInReason`: a sign-in page only by its top-level URL, a visible password / email-entry form in the main frame, or a visible Apple sign-in frame (idmsa / appleid); other frames never count. Each detection logs "sign-in detected because: <rule> at <url>"; heartbeats name the page; a copied session that lands on a sign-in URL is said plainly ("did not carry over"); the watchdog dumps the sign-in wait once after `--step-timeout`, `--debug-dump` up to 3 times (`tests/console-fill.test.ts`, chooser / home with the widget frames, a top-level accounts.google.com page). |
| 15 | 2026-10 | The real App Privacy "Data Collection" dialog names each data type checkbox by its label run together with its help text ("NameIncluding first or last name"), and labels some types differently from Apple's help page ("Purchases", "Other Data"): exact-name matching found none. | `labelNames` (exact name, then the label as a prefix that ends where the help text starts) and `TYPE_ALIASES` in `cli/lib/console-forms.ts`; the flow fixture mirrors the real dialog's structure (`tests/console-flow.test.ts`). |