Skip to content

macOS: the Keychain grant is broader than the agent's own credential #242

Closed

Description

@Starefossen

needs_keychain() is true for Copilot, Gemini, Antigravity and Claude (src/agent.rs:273). The resulting profile grants read/write on ~/Library/Keychains (sandbox_profile.rs:466), unrestricted Mach-service lookup including securityd (:490), and execution of /usr/bin/security.

This is not host RCE and should not be described as one. The database stays encrypted and securityd arbitrates every request, so the grant does not hand over the user's secrets wholesale. What it does is put the sandboxed agent in the position of any local process asking securityd for an item — and per-item ACLs decide the rest.

An item comes out silently when its ACL allows all applications, explicitly trusts /usr/bin/security, trusts the requesting binary, or the user previously chose "Always Allow", and the item carries no user-presence requirement. Items gated on Touch ID or a password are not silently extractable. So the exposure is real but bounded by how each item was created, which is not something cplt controls or can see.

security find-generic-password -s SERVICE -a ACCOUNT -w
security find-internet-password -s host.example.com -a ACCOUNT -w

Anything retrieved can then be used directly, or exfiltrated over the permitted HTTPS egress. Non-exportable private keys stay non-exportable, but signing and decryption with them may still be permitted.

Fix

A scoped credential broker: retrieve the specific token the agent needs before entering the sandbox, and drop the Keychain grant entirely, rather than granting access to the whole Keychain service for the session.

#173 already does this for Copilot ("pre-extract the Copilot token so the Keychain grant can be dropped"). It has been open since 14 August as an auth cleanup; it is the remediation for this, and is worth re-prioritising on that basis. The remaining work is the same treatment for Gemini, Antigravity and Claude, and then removing needs_keychain() where nothing depends on it.

Worth confirming per agent before dropping a grant: some agents re-authenticate mid-session or refresh tokens, in which case a one-shot pre-extraction is not sufficient and the broker needs to stay reachable.

Activity

  1. added
    securitySecurity defect, hardening, or advisory follow-up
    on Sep 6, 2026
  2. Starefossen commented on Sep 22, 2026

    @Starefossen
    MemberAuthor

    Blocked on #277.

    The drop mechanism landed in #257: the Keychain grant is dropped when the agent's credential is in the environment. Copilot still keeps the grant, because its keychain_substitute_env_vars() entry is empty on main (src/agent.rs) until the injected-token probe in #277 shows Copilot authenticates under a grant-dropped profile.

    Even with a positive probe, users authenticated through gh would keep the grant: credential_outside_keychain checks only the parent environment, not the token extract_gh_token() pulls later (see the last comment on #277).

  3. Starefossen commented on Oct 15, 2026

    @Starefossen
    MemberAuthor

    Superseded by #695 and docs/copilot-auth-plan.md. Copilot's Keychain grant was dropped by default in #696, Claude's OAuth path landed in #700, and selected-account validation landed in #772.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity defect, hardening, or advisory follow-up

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions