openvibers/openvibe.locker Commits

Document the blind fill flow (#10)

OP OpenVibers committed 4 days ago · 7a41ceffcf · 1 file, +37 −35

* Document shipped blind fill flow

* Document blind fill execution safeguards

Changes

ddiff --git a/docs/BLIND-FILL.md b/docs/BLIND-FILL.md
iindex bcf0429..f063e29 100644
---- a/docs/BLIND-FILL.md
++++ b/docs/BLIND-FILL.md
@@@ -1,43 +1,45 @@
--# Blind fill (phase 2 design only)
++# Blind fill
--Blind fill lets a person approve a sign-in in a trusted Locker UI while a Computer session operates a browser. It waits
--for Run's control lease and is not implemented by Locker's password manager.
++Blind fill lets an agent request a sign-in for the browser in its OpenVibe.Run session. The person approves in the
++Companion extension, which reads one saved Locker login, seals the selected fields to the target machine's Link Send
++public key, and sends only that envelope to Run. The agent receives only `{ ok, reason }`.
## Flow
--1. A Computer asks for a saved login by exact origin. It receives only a request id, origin, and the site title; it
-- never receives a login, password, TOTP seed, or decrypted Locker item.
--2. Run pauses the Computer browser under its control lease and sends a B2 approval request to the person's trusted
-- device. The trusted device displays the Computer, exact origin, requested account label, and the requested fields.
--3. The person approves or denies. Approval unlocks the selected login in the trusted device's extension and creates a
-- short-lived, single-use fill grant scoped to that exact origin and those fields.
--4. Link/Run delivers the grant to the Companion extension through its authenticated channel. The extension checks the
-- active tab origin and frame origin again, fills only the approved fields in the top-level page, and submits only if
-- the person approved submission. It never fills a cross-origin iframe.
--5. The extension discards plaintext and grant state after use or timeout, and locks after 15 minutes idle. The agent
-- tool `locker_fill_login` returns only a success/failure result; it never returns field values or secrets.
++1. The session controller calls `POST /api/v1/sessions/:id/fill-requests` with an origin. Run reads the active tab's
++ top-level origin from the machine and requires an exact scheme, host, and port match. Run accepts only a machine the
++ person owns or OpenVibe's own pool, pauses the agent's control lease, and expires the request after two minutes.
++2. Run notifies the person with an **Approve a sign-in** action. Companion lists the person's pending requests with
++ the agent, computer, exact origin, and fields that will be typed. Companion uses the `run.fill.approve` Dock scope.
++3. The person unlocks Locker in Companion, selects a login for that exact origin, and approves username, password, an
++ optional TOTP code computed locally, and whether to submit. Companion shows the machine's Link Send key thumbprint.
++ It seals `{ request_id, origin, username, password, code?, submit, exp }` for that machine; the grant expires in
++ sixty seconds. Only the envelope is posted to Run, and each request can be granted once.
++4. Run sends the envelope over its existing machine control channel. Node opens it with its Link Send private key and
++ checks request id, expiry, and exact origin. Origins must be HTTPS and match exactly, including host and port; HTTP
++ pages are refused at request and fill time. Node checks the active CDP top-level frame origin again, creates a CDP
++ isolated world for that frame with universal access disabled, and evaluates the fill script there. The script checks
++ `location.origin` again, fills visible inputs in the top-level document only, and submits only when approved. Iframe
++ inputs are never searched. Node retains used request ids for two minutes and returns `reused` on replay. Node reports
++ `ok` or `failed`; Run resumes the agent after navigation or five seconds.
++5. Run refuses screenshots and page text while approval or fill is active and until the next navigation. Fill audit
++ records contain request id, origin, decision, outcome, and times only.
--## Required controls
++## Security requirements
--- The password manager extension unlocks Locker locally with the same passphrase, passkey, or recovery-kit flow.
--- A saved login is eligible only when its stored origin exactly matches the requested origin or the person explicitly
-- added that second origin. No wildcard domains, redirects, subdomains, or cross-origin frames inherit access.
--- Approval names the target Computer, origin, account label, and fields. Each fill needs a fresh approval; approval
-- cannot be replayed, transferred to another tab, or used after its short expiry.
--- The browser integration must bind its grant to the active tab and top-level frame, verify the origin immediately
-- before input, and abort if navigation or focus changes.
--- The Locker server, Run, Link, MCP response, and agent context receive no plaintext. The extension is the only
-- component that decrypts and types the selected fields.
--- Audit records contain request id, origin, decision, and time only. Do not record field contents, credential ids,
-- TOTP codes, page HTML, or screenshots containing secrets.
--- TOTP remains locally computed. If a site needs an OTP, show that as a separately approved field in the trusted
-- prompt; never expose it to the agent.
++- The agent never receives a login, password, TOTP seed or code, decrypted item, envelope, page HTML, or screenshot
++ captured during the protected interval.
++- Requests require the agent to hold the session control lease. Partner, community, and external machines cannot fill.
++- The requested origin and the machine's current top-level origin must match exactly at request and fill time. A
++ cross-origin iframe is never eligible.
++- Grants are encrypted to the chosen machine, expire after sixty seconds, and are single-use. Mutable plaintext buffers
++ are cleared after the machine hands the fields to its local browser plugin. Python strings are immutable and cannot
++ be overwritten; the plugin drops its plaintext local references as soon as the call returns.
++- Audit data contains no field contents, credential ids, codes, page HTML, or screenshots.
++- `locker_fill_login(origin)` returns only `{ ok, reason }`.
--## Open decisions before implementation
++## Verification
--- Run's control lease must provide a race-free pause/resume and identify the exact browser tab and navigation epoch.
--- Link needs an authenticated, single-use channel between the trusted extension and the Companion running in the
-- Computer browser.
--- The B2 request contract must support origin and field scopes without allowing an agent to impersonate the trusted
-- prompt.
--- Product review must confirm the approval copy, timeout, recovery behavior, and extension permission model.
++The implementation tests origin mismatch at request and fill, expiry and replay, owner/pool eligibility, refusal of
++community machines, response secrecy, screenshot withholding, iframe refusal, and cross-language Locker envelope
++vectors generated by the JavaScript SDK.