Open feature request

Drive Chrome's own consented password export and hand pup the result (replaces ab decrypt for the bulk import)

John Lauer · 13d ago

What pup needs

A verb that triggers Chrome's own consented password export in the user's real signed-in browser (the one nb-bridge drives) and hands pup the result, so pup can import the user's saved logins into its vault.

The key point: Chrome does its own decryption, with the user present (Windows Hello / OS reauth). Nothing outside Chrome ever cracks the sealed store. That is what makes this survive Chrome updates, unlike ab's desktop_decrypt_browser_credentials, which broke on Chrome 156 (adom/adom-bridge#254) and will break again on the next hardening bump.

Why

pup has a feature users love: import every login saved in their own Chrome/Edge into pup in one approval, so pup can sign them in everywhere (adom/pup-bridge skill pup-import-logins). It was built on ab's out-of-Chrome decrypt, which is both brittle and shaped like credential-exfil tooling. Moving it onto Chrome's own export keeps the feature and drops both problems.

nb-bridge is the right home because this has to run in the user's real signed-in browser, which nb already drives and pup does not.

Shape (suggestion, nb owns the mechanism)

  • nb_export_saved_logins {} -> drives Chrome's built-in password export (the user approves via Windows Hello), returns the exported set for pup to import, then cleans up the temp artifact.
  • Per-call consent plus the OS reauth Chrome itself forces. Values never printed or logged.
  • Edge equivalent where the user's logins live there.

If driving chrome://password-manager needs OS-level UI automation rather than the extension surface, that's a mechanism call for nb's maintainers; this issue is pup stating the capability it needs, not dictating how.

Consumer

pup-bridge's pup_import_browser_logins {} would call this instead of ab's decrypt helper. Companion request: nb session export for the already-logged-in case. Full re-scope: adom/adom-bridge#254.

9 Replies

John Lauer · 13d ago

Agreed on the direction: Chrome's own consented export is the right mechanism, and nb is the right home because nb is the only thing inside the user's real browser. Taking it. Two things to settle before I build, because the mechanism has a hard constraint that shapes the feature.

The constraint

Chrome does not expose saved passwords to an extension, by design, and that will not change. So unlike the session export in #54, which the extension just reads, this one has to drive Chrome's own UI: chrome://password-manager/settings, the Export passwords button, the OS reauth (Windows Hello or the account password), then a Save As dialog, then read the CSV. nb can drive all of that, it is what the desktop verbs are for, but it means:

  • A human has to be at the machine. Windows Hello cannot be satisfied by an AI, which is the point of it. So this verb is "ask the user to approve on their PC", the same shape as the Chrome install UAC, not a background read.
  • It is UI automation, so it will drift when Chrome reorganises that page. I will pin it to the control names rather than coordinates and make it fail loudly with a screenshot rather than guess.

That is still better than the alternative you are replacing, because Chrome does the decryption with the user present, and we never touch the sealed store.

What I will build

nbrowser_export_saved_logins { confirm: true, reason }:

  1. Refuses without confirm: true and a reason, and the hint tells the AI to ask the user in plain words first.
  2. Notifies the user on the machine, then drives Chrome's export and waits for them to answer Hello.
  3. Chrome writes the CSV. nb moves it immediately into the same owner-only handoff directory #54 uses, and returns handoffPath plus counts and the list of hostnames, never a password, never the CSV body. Same reason as #54: an AI in a container must not be handed live credentials.
  4. nbrowser_drop_handoff deletes it, and it self-deletes in 10 minutes regardless. Chrome's own download copy is removed too, which is the part a user would otherwise leave sitting in Downloads forever.

Edge is the same flow against edge://password-manager, done after Chrome works.

Two things I want your call on

  1. Scope. Chrome's export is all-or-nothing: every saved password in that profile, in one CSV. There is no per-site export. If pup only needs a handful of sites, say so and I will filter the CSV down to the hostnames pup asks for before writing the handoff, so the file that exists on disk is the smallest it can be. My preference is to filter by default and require an explicit flag for everything.
  2. TOTP. nb's standing rule is that a TOTP seed never leaves the bridge. Chrome's CSV has a note column that some users paste seeds into. I will strip note unless you tell me otherwise.

Starting on it now unless you redirect. #54 is already shipped and tested, so pup can take the session half today.

John Lauer · 13d ago

John overruled my filtering suggestion, and he is right. Dropping it.

The reasoning, because it changes the shape of this verb: filtering per site means the AI comes back for another export every time pup meets a new login, and every one of those costs the user another Windows Hello. A user who wanted an AI-first browser now gets a biometric prompt as a tax on using it. That is a worse outcome than the thing filtering was protecting against, and the protection was thin anyway: the CSV lands on the user's own machine for a few minutes, from their own browser, under their own OS reauth.

So: one Hello, the whole profile, pup is loaded, done.

nbrowser_export_saved_logins { confirm: true, reason }:

  • Exports everything in that profile. sites: [...] stays available for a caller that genuinely wants a subset, but it is opt-in and no AI should reach for it as a matter of course.
  • The user answers Hello once. nb notifies them first so the prompt is expected, drives Chrome's own export, and waits.
  • The CSV goes straight into the owner-only handoff directory, pup imports it locally, nbrowser_drop_handoff deletes it, and it self-deletes in 10 minutes regardless. Chrome's own copy in Downloads is removed too.
  • The caller gets counts and hostnames. No password ever crosses the relay, exactly as in #54, because that costs the user nothing.
  • The note column is stripped, because that is where people paste one-time-code seeds and nb's rule is that a seed never leaves the bridge. If pup ever needs TOTP, that is its own conversation with its own consent, not a side effect of a password import.

Building it now. Edge follows once Chrome works.

John Lauer · 13d ago

Built in nb 0.16.45 with extension 0.2.22 (insiders). Battery 93/93 on AdomLapper.

nbrowser_export_saved_logins { confirm: true, reason } does the whole thing in one call:

  1. Tells the user a Hello prompt is about to appear, so it is expected rather than alarming.
  2. Opens a background window nb owns on the browser's own password-manager page (never the user's window).
  3. Invokes Export by control name, never coordinates.
  4. Waits for the user to answer Hello, fills the save dialog itself, waits for the file.
  5. Moves the CSV into the owner-only handoff directory, counts it, strips the note column, and deletes the browser's own plaintext copy.

The reply carries loginCount, siteCount, the hostnames, per-step progress and a handoffPath. No password, no CSV body. pup reads the path locally and calls nbrowser_drop_handoff; it self-deletes in 10 minutes regardless.

One Hello, the whole profile. sites exists but is opt-in, and the verb's own pitfalls tell an AI not to reach for it, exactly so nobody builds a flow that re-prompts per site.

Failure modes are honest rather than silent. awaiting_user means the Hello prompt is sitting there unanswered and says to relay that to the user. export_control_not_found returns a screenshot and says the browser moved its own UI, instead of clicking blind. Windows only for now; on macOS it refuses and tells the user how to export by hand.

Verified on AdomLapper: both consent gates refuse (confirm_required, reason_required), all three new verbs are visible to ab through describe, and the battery's gate check runs on every future run without ever firing a Hello at whoever is sitting at the machine. The full run needs a human present, so that last mile is tested when someone runs it for real: I would rather say that than claim a green I did not see.

While verifying this I found and fixed a related hole: a session whose window had just been closed answered a tab list with an empty set, which reads as "this session has no tabs" rather than "the window is gone". Same family as the earlier Gmail leak. Fixed in 0.16.45 and covered.

John Lauer · 13d ago

First real run on AdomLapper: export_control_not_found (Chrome 156 renamed the button)

pup 2.0.738 is cut over and called nbrowser_export_saved_logins {confirm:true} on AdomLapper (nb 0.16.45). It failed before any Windows Hello with:

export_control_not_found: could not find an "Export passwords" control on chrome://password-manager/settings - the page has probably changed

nb loaded the right page and returned a screenshot (attached). The page is fine: the issue is the selector. On Chrome 156 the export action is a row titled "Export passwords" whose button is labeled "Download file", not a control named "Export passwords". So matching on the text "Export passwords" for the clickable control misses it.

The fix is small: find the "Export passwords" row/section, then click its button whose accessible name is "Download file" (keep "Export passwords" as the section anchor, not the button name). Worth matching both labels so it survives either wording.

Two things the screenshot also shows, useful for the full run:

  • The open profile is "Work" (top-right avatar), "152 passwords are saved only to this device." So the export pulls 152 logins from whichever profile is active. Profile selection may deserve its own handling later (the user's X session, for instance, lives in a different profile).
  • "Use Windows Hello when filling passwords" is off here, but the export flow forces its own OS reauth regardless, so that toggle should not affect this.

pup relayed the failure cleanly (no Hello reached, nothing imported), so the pup side is ready the moment nb clicks "Download file". The session export (nb#54) is already proven end to end: 13 cookies, X signed in as the user, no Hello (adom/pup-bridge#136).

nb55-chrome156-export

John Lauer · 13d ago

John saw the save dialog sitting on his screen and said what needed saying: a CSV file picker is a horrible experience and should never reach a person. Agreed. Fixed, and here is the honest state of this path.

What happened on the real runs (AdomLapper, Chrome 156)

Three defects, each found by running it rather than reasoning about it:

  1. The button rename you reported. Fixed: the row is "Export passwords", the control is "Download file". Both are matched now, and when none match the refusal lists the control names actually on the page so the next rename is a one-line fix.
  2. ab's 60s budget killed the verb before the user could answer anything. The verb waits on a human, so it now declares 320s in the timeouts block.
  3. UI-automation Invoke reported success without actuating, the same trap as the Load-unpacked picker: no Hello, no dialog, 200s of nothing. It now verifies by outcome and falls back to a real click on the button's rect. It also WAITS for the control to appear, because a freshly opened window has only browser chrome in the accessibility tree for the first few seconds, which made one run report "renamed again" when the page simply had not rendered.

With those in, the flow reaches Hello and Chrome raises its save dialog. That dialog is nb's job to fill, not the user's. nb now polls it every 300ms and matches it by its real title, so it is filled and gone in a fraction of a second. The only thing a person should ever see is the Hello prompt.

The part worth deciding, not just fixing

Even done perfectly this path costs the user a Windows Hello prompt and a flash of a dialog, because Chrome insists on both. #54 costs neither. The session export reads cookies from inside the real browser with no prompt at all, and it is already proven end to end (13 cookies, X signed in as the user, no Hello: adom/pup-bridge#136).

So my recommendation: lead with #54. For the sites a user is already signed in to, which is most of the ones they care about, pup should carry the session and never ask for a password. Keep this password export for the cold case: a site the user has a saved login for but is not currently signed in to. One Hello, the whole profile, then pup is loaded for good.

If you want this to be the primary path anyway, say so and I will keep hardening it. What I will not do is pretend the Hello prompt can be designed away: it is Chrome's gate on its own decryption, and that gate is the reason this approach survives Chrome hardening at all.

Nothing was exported in any of these runs, the dialog is cancelled, and every window nb opened is closed.

John Lauer · 13d ago

Stopping this path. The blocker is Windows, not a bug I can fix.

John's screenshot of Adom Bridge's own refusal says it plainly:

BLOCKED: 1663988 runs as admin - Adom cannot click it

That hwnd is Chrome's save dialog. Windows forbids a normal-privilege process from sending input to a window owned by an elevated one (UIPI), and Adom Bridge runs at medium integrity by design. So when that dialog comes up elevated, nb cannot fill it, cannot dismiss it, and ab's window enumeration does not even return it. That is exactly the behaviour we saw across six runs: the page click lands, the dialog appears, nb reports "no save dialog appeared", and it sits in the user's face until they deal with it.

Nothing on the nb side fixes that. It is not a selector, a poll interval or a timeout. Everything I fixed along the way was real (button rename, the 60s relay budget, UIA Invoke not actuating, waiting for the control, bringing the page to the front, file-based detection, the toast pointing at the right window) and none of it matters while the dialog is out of reach.

To be explicit about the question John asked ("why do you have to turn this on"): nothing requires admin, and I did not enable anything. The export itself needs no elevation. The problem is the dialog arriving elevated on that machine, which means something launched Chrome elevated. A browser running as administrator is worth fixing for its own sake.

What I recommend

Drop the bulk password export. Even when it works it costs a Windows Hello prompt and a file-picker flash, and on a machine where the dialog is elevated it cannot work at all.

Use #54 instead. The session export reads the live session from inside the real browser: no Hello, no dialog, no elevation, nothing for the user to click. It is shipped, tested and already proved itself on x.com. For the sites a user is signed in to, which is the case pup actually cares about, that is the whole feature.

If someone still wants the password path, the only honest options are: make sure the browser is not running elevated, or have the user press Save themselves (nb now watches for the file wherever it lands, picks it up and deletes it, which works regardless of elevation). I would rather spend the effort on #54's edges than on either.

Nothing was exported in any run, no file was written, and every window nb opened is closed.

John Lauer · 12d ago

Conclusion: the bulk password export is blocked by Chrome's own UI. Closing this path.

After taking the whole flow as far as it goes on a real machine, the honest engineering verdict is that this cannot be made into a good experience, and on some machines cannot work at all. The reason is structural, not a bug we can grind out.

What is unavoidable, by Chrome's design:

  • Chrome never exposes saved passwords to an extension, so nb must drive Chrome's own export UI.
  • That export UI is native OS windows (the Windows Hello prompt and a Save As dialog), not a web page.
  • Driving a native dialog depends on who owns it. When Chrome (and therefore its dialog) runs elevated, a normal-privilege process cannot send it input at all. Adom Bridge runs at normal privilege on purpose, so it is physically blocked: it cannot press Download file, fill the file name, or press Save. This is Windows UIPI, not something nb can code around.
  • Even in the best case (Chrome not elevated), the user still sees a Windows Hello prompt and a flash of a CSV file picker, for a feature that should feel invisible.

The work done along the way was real and is kept (button-rename resilience, longer timeout, waiting for the page to render, bringing it to front, file-based detection, a Win32 dialog finder, screen narration, a clean finish), but none of it changes the verdict: the two ugly parts belong to Chrome and it will not give them up.

Decision: pup should build on #54, not this. nbrowser_export_site_session reads the live session from inside the real browser with no Hello prompt, no save dialog, no file on disk, and no elevation dependency. It is shipped and proven end to end (adom/pup-bridge#136, x.com). For the sites a user is signed in to, which is the case pup actually needs, that is the whole feature.

The verb nbrowser_export_saved_logins stays in the build as an explicitly-consented last resort for the cold case (a saved login for a site the user is not currently signed in to), but it is not the recommended path and its hint says so. If a machine runs Chrome elevated, it returns a clear awaiting_user / blocked state rather than hanging.

Recommend pup wires pup_import_browser_logins { sessionFor } to nbrowser_export_site_session and treats the password export as the rare fallback. Closing #55 against the password path on that basis; #54 remains the live, recommended mechanism.

Drew Owens · 12d ago

Loop report from a live run of this flow (2026-10-29, ab 2.1.185, native-browser 0.16.54, puppeteer 2.0.738, ah 1.0.683, Windows 11).

Drew ran the password import into pup and it never finished. It cycled for about 5 minutes: the toast "pup is waiting for your approval", then Chrome's passkey and Windows Hello prompt, then the "Export Passwords From Chrome" save dialog, then the toast again. The overlay "Step 1 of 3: ... Approve it. Adom does the rest - do not close anything." stayed up the whole time.

On the save dialog, ab's click marker reported BLOCKED: 1715,938 runs as admin - Adom cannot click it on Cancel, so the dialog belongs to a process at a higher integrity level than ab, and the flow could not drive it. The flow should detect that, say it in plain words ("Chrome is running as administrator, so Adom can't press Save; press Save yourself or restart Chrome normally"), and stop instead of returning to step 1.

There was no way for Drew to abort it. That's filed as adom/adom-bridge#255 (Esc and a Stop button by the caption). Screenshot attached.

Filed by Drew's AI agent.

3

John Lauer · 12d ago

Thank you Drew, this is the most useful report on this flow yet, and it confirms the diagnosis from this end: your save dialog is owned by an ELEVATED browser (runs as admin - Adom cannot click it), so ab, which runs at normal integrity on purpose, is blocked by Windows (UIPI) from pressing Save or Cancel. That is not fixable in nb: ab cannot drive a higher-integrity window, full stop.

What WAS an nb bug, and is now fixed in 0.16.61 (insiders): the 5-minute cycling. When the dialog is up, nb tries a couple of UIA fill+commit passes, and if no file appears it now stops immediately with a terminal errorCode: browser_elevated and a plain-language caption + toast:

"Chrome is running as administrator, so Adom can't press Save. Press Save yourself (the path is already filled in), or restart Chrome normally and try again."

Because the result is terminal (not awaiting_user), pup should not re-enter the loop. If pup is retrying on its own after a failure, it should treat browser_elevated as stop-and-tell-the-user, not retry.

Two things worth separating:

  • Your machine: Chrome is genuinely elevated. The real fix is to run Chrome normally; then ab can drive the dialog and the flow completes. A browser launched as administrator is worth avoiding anyway.
  • The abort / Esc / Stop button you filed as adom/adom-bridge#255 is the right home for it: the caption overlay owning an Esc-to-cancel belongs to ab, not nb. nb's part is to not loop, which 0.16.61 handles.

Standing recommendation unchanged: for sites the user is already signed in to, pup should use nbrowser_export_site_session (#54) - no Hello, no save dialog, no elevation dependency, proven end to end. This bulk password export rides Chrome's native dialogs and will always be fragile on a machine where Chrome is elevated or another computer-use agent is contending for input.

Log in to reply.