Skip to content

feat: fill password fields from the Keychain or with approved agent text, and refuse typing or pasting into them otherwise - #122

Closed
iamnbutler wants to merge 7 commits into
feat/caller-approvalfrom
feat/password-fill
Closed

iamnbutler wants to merge 7 commits into
feat/caller-approvalfrom
feat/password-fill

Conversation

@iamnbutler

@iamnbutler iamnbutler commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Implements #117. Stacked on #120 (caller approval): it uses #120's prompt and caller identity, and lands after it.

desktop_fill_password fills the password field that has keyboard focus in a snapshot.

  • Keychain by reference. service names a generic password and server an internet password, with an optional account. The runtime reads the item with SecItemCopyMatching, so macOS asks the user for each item, and the agent never sees it.
    • It reads attributes first, so no match or several matches are refused without a prompt.
    • It waits up to 60 s, and denial or cancel is a refusal.
    • An internet item fills only into a web area whose URL host is its server.
  • Agent text. A per-fill Desktop Tools prompt, through feat: ask once per agent before it controls the Mac, and keep agent input off Desktop Tools' own UI (#24) #120's Confirmations, names the caller (Callers.shared), the app and the site. Always Allow Agent-Supplied Passwords in the menu skips it; it is off by default. After 60 s the prompt is withdrawn with cancel(key:) and the fill is refused.
  • Delivery. Clear, then Unicode key events at the observed focus. The host rechecks that the field is still focused after the prompt or Keychain read.
  • Result. Completed only when the field's value is all mask characters, as many as the secret has; otherwise unknown. No unit counts, and error messages that could count characters are replaced.
  • Refused:
    • SecurityAgent, LocalAuthentication, loginwindow and coreautha targets;
    • a field that isn't secure or isn't the snapshot's focus;
    • control characters.
  • No leaks. The secret is never in results, logs or activity, which records only "Fill password" with the caller and target. The TS layer never echoes the text, and a test checks it.

Behavior change. While the live focused element is secure:

  • desktop_key refuses keys that enter characters and Command-V with any modifiers.
  • desktop_insert refuses every route: type, paste, and attachments.
  • Both refuse before input, with SECURE_FIELD_REQUIRES_FILL and a hint naming the fill tool.
  • desktop_type, set_value and select on a secure element now use the same code.
  • Return, Tab, Escape, arrows, Delete and other shortcuts still pass.
  • desktop_menu refuses a Paste command the same way. The host matches the resolved leaf by AXMenuItemCmdChar V with Command, or an AXIdentifier starting with paste, so localized titles are covered. desktop_menubar_click only opens a status item and never chooses a menu item, so it needs no check.
  • Documented gaps: a context-menu Paste click through desktop_click, and drag-and-drop.

Native.

  • peekaboo-secure-fill.patch comes last, after caller-admission. It adds the secureTextFill op, its hook, the guard in handleAuthorized, and HotkeyService.entersText.
  • The runtime adds password.swift with desktop_tools_set_password_autofill, desktop_tools_set_confirmation_handlers(ask, cancel) and desktop_tools_answer_confirmation.

Tests.

  • npm test: 77 pass, including a new password-fill test. npm run check is clean.
  • Peekaboo: HotkeyEntersTextTests, PeekabooBridgeRequestPlanTests, PeekabooBridgeOperationSemanticPlanTests and PeekabooBridgeOperationResultSemanticsTests pass (74 tests). The wire count is 134.
  • Live checks are pending a dev app at the shared fixed path: Keychain fill, approved text fill in Chrome, the key/insert/paste refusals, SecurityAgent refused, and activity with no secret.

@iamnbutler
iamnbutler force-pushed the feat/caller-approval branch from 2335773 to 218fc99 Compare October 17, 2026 12:23
@iamnbutler
iamnbutler force-pushed the feat/caller-approval branch from 218fc99 to 0d8b806 Compare October 17, 2026 12:40
@iamnbutler
iamnbutler deleted the branch feat/caller-approval October 17, 2026 12:48
@iamnbutler iamnbutler closed this Oct 17, 2026
iamnbutler added a commit that referenced this pull request Oct 17, 2026
@iamnbutler

Copy link
Copy Markdown
Contributor Author

Replaced by #124: this PR closed when its base branch was deleted after #120 merged.

iamnbutler added a commit that referenced this pull request Oct 17, 2026
iamnbutler added a commit that referenced this pull request Oct 17, 2026
iamnbutler added a commit that referenced this pull request Oct 17, 2026
…ext, and refuse typing or pasting into them otherwise (#124)

* feat: fill password fields from the Keychain or with approved agent text, and refuse typing or pasting into them otherwise (#117)

* docs: link #122

* feat: refuse Paste menu commands while a password field has focus

* chore: regenerate the secure fill patch on main

* docs: link #124

* chore: regenerate the secure fill patch on main

* chore: regenerate the secure fill patch on main

* fix: report password-field refusals as refused, with SECURE_FIELD_REQUIRES_FILL or FILL_REFUSED
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant