diff options
| author | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-16 11:55:27 -0400 |
|---|---|---|
| committer | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-16 11:55:27 -0400 |
| commit | ad78b41140f9040094295a28c5ab6482483efa4f (patch) | |
| tree | ba36801d51069779d76fe12ed94c9e73c343cdaa /README.md | |
| parent | 7992115c6dd9884e1a1f2992db531342d2916fcd (diff) | |
0.16.0: mandatory pairing + signed envelopes + cross-process state coherence
Major security hardening of the Chrome bridge driven by red-team findings.
- Mandatory pairing via /chrome onboard (one-time per machine). Bridge pins exact
chrome-extension:// origin and derives HMAC keys via HKDF. Until paired, the
bridge returns idle responses and refuses to deliver commands.
- Signed envelopes on /next, /result, /command. HMAC-SHA256 over protocol version,
direction, method, path, extension ID, bridge ID, timestamp, fresh nonce, and
SHA-256 of the body. +/-30s clock window; 5-min LRU nonce replay defense on
both request and response directions; constant-time MAC compare.
- Peer Pi sessions sharing the bridge sign each /command with a broker HKDF key
(no bearer token transmitted) and verify the owner's signed response.
- Global loopback enforcement. PI_CHROME_BRIDGE_HOST=0.0.0.0 ignored unless
PI_CHROME_BRIDGE_DANGEROUS_REMOTE=1; loopback check still applies per-endpoint.
- Cross-process state coherence: fs.watch + per-request mtime check on
~/.config/pi/chrome-bridge.json. A peer Pi's unpair (or any external rm/edit)
propagates to the owner's in-memory state before the next privileged verify;
replay caches reset on key rotation.
- Body cap 1 MiB -> 413. Queue/pending/waiter caps. Malformed JSON -> 400.
Non-object JSON bodies -> 400 instead of being trusted.
- page.navigate initScript: try/finally around tabs.update, per-call onCommitted
listener with bounded commit-wait window, one-shot delete in global onCommitted.
No more stale init scripts firing on later unrelated navigations.
- crypto.randomUUID() command IDs.
- Manifest diet (dropped activeTab); added popup.html/popup.js for the pair UI.
UX:
- /chrome onboard runs install + pair in one interactive flow.
- /chrome help reorganized into First-time / Day-to-day / Maintenance.
- Status bar lights up as green dot only when authorized.
- Early console/network capture at document_start so boot-time React errors and
initial API calls aren't dropped (ported from DaniBedz/pi-chrome fork).
Docs:
- SECURITY.md threat model rewritten with in-scope / out-of-scope sections.
- README rewritten with the install-then-pair flow front and center.
- CHANGELOG 0.16.0 entry with full patch list.
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 42 |
1 files changed, 35 insertions, 7 deletions
@@ -21,28 +21,56 @@ You: [keeps coding — agent never asked you to log in] pi install npm:pi-chrome ``` -Then in Pi: +Then in Pi (do this **once per machine**): ```text /chrome onboard ``` -On macOS this opens `chrome://extensions`, reveals the bundled `browser-extension/` folder in Finder, and copies its path to your clipboard. In Chrome: **Developer mode** → **Load unpacked** → paste the path. Done. +Walks you through a two-step interactive flow: -Verify, then authorize current Pi session from the terminal: +1. **Install the Chrome extension.** On macOS, pi-chrome opens `chrome://extensions`, reveals the bundled `browser-extension/` folder in Finder, and copies its path to your clipboard. In Chrome: **Developer mode** → **Load unpacked** → paste the path. Press Enter in Pi when the extension shows up. +2. **Pair the extension with this Pi bridge.** Pi mints a one-time `pcp_…` invite (also copied to clipboard). Click the **Pi Chrome Connector** toolbar icon in Chrome, paste the invite into the popup, and click *Pair*. Press Enter in Pi to confirm. + +Pairing binds your specific Chrome extension instance to this Pi config via an HMAC handshake. Without it the bridge cannot tell your extension apart from any other Chrome extension on the same browser, so it refuses to deliver any commands at all. See [Why pairing?](#why-pairing) below. + +Then, in any Pi session where you want to use chrome_* tools: + +```text +/chrome authorize 15m # or 30m, or indefinite +``` + +Verify anytime: ```text /chrome doctor -/chrome authorize ``` ```text -Performing Chrome bridge health check pi-chrome v<version> -• Local bridge: mode=server, url=http://127.0.0.1:17318 -✓ Companion Chrome extension responding (ID: <chrome-extension-id>, ext v<version>) +• This pi session is running the Chrome connection for this machine. +✓ Bridge paired with extension <chrome-extension-id> (bridgeId <id>). +✓ Chrome is connected (companion extension v<version>, responded in <ms>ms). +✓ pi-chrome can run code in the active Chrome tab. ``` +### Why pairing? + +The bridge listens on `127.0.0.1:17318`. Any Chrome extension with localhost host permissions — not just pi-chrome — can `fetch()` that port; Chrome's origin model blocks ordinary web pages but not extension code. Without an explicit pairing step, an unrelated extension could poll for queued commands (which include selectors, typed text, eval source, file-upload paths) or post forged results. `/chrome onboard` fixes that by deriving a shared HMAC key during the popup paste step and pinning the exact extension ID. After that, every `/next` / `/result` / `/command` request must be signed; replays and mismatched origins are rejected. Same-user malware that can read `~/.config/pi/chrome-bridge.json` is explicitly out of scope; see [SECURITY.md](./SECURITY.md). + +### Day-to-day commands + +| Command | When to use | +|---|---| +| `/chrome authorize [15m\|30m\|<min>\|indefinite]` | Per Pi session, unlock chrome_* tools. | +| `/chrome revoke` | Lock chrome_* tools again. | +| `/chrome status` | One-line summary: connection / auth / background mode. | +| `/chrome doctor` | Full health check; tells you exactly what's missing. | +| `/chrome onboard` | First-time setup, or re-install + re-pair after a Chrome profile move. | +| `/chrome pair` | Re-pair an already-installed extension (subset of `onboard`). | +| `/chrome unpair` | Clear pairing keys; every Pi session will need to re-pair. | +| `/chrome background [on\|off\|status\|toggle]` | Run without focusing Chrome. | + --- ## Try this in 30 seconds after install |
