Uh oh!
There was an error while loading. Please reload this page.
feat(desktop): import cookies from Safari - #7262
Conversation
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Uh oh!
There was an error while loading. Please reload this page.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
88cf9f7 to
31db6abCompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — Adds a substantial macOS Safari cookie-import capability with sensitive cookie-file access, a new Full Disk Access UI/IPC flow, and cross-platform browser-detection changes. The new test file also adds a file-level static-analysis suppression directive, so the change requires human review. No code changes detected at You can add or adjust custom eligibility rules. Learn more. |
31db6ab to
3df1bbeCompare3df1bbe to
79df949Compare79df949 to
d66d4fbCompareThere was a problem hiding this comment.
Effect service conventions review: one finding on the new Safari cookie error. Everything else (subpath namespace imports, Effect.fn boundaries, tagged-error definition, pass-through of the already structured error in Effect.try, no hidden runtimes) matches the conventions.
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
d66d4fb to
06d5c22CompareThere was a problem hiding this comment.
Effect service conventions review: one finding on the new Safari cookie error. Everything else (subpath namespace imports, Effect.fn boundaries, tagged-error definition, pass-through of the already structured error in Effect.try, no hidden runtimes) matches the conventions.
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
06d5c22 to
06722a1Compare06722a1 to
a8129c1CompareUh oh!
There was an error while loading. Please reload this page.
a8129c1 to
be13d9fCompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
Bugbot Autofix is ON, but a cloud agent failed to start.
Reviewed by Cursor Bugbot for commit 6918217. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Safari does not encrypt its cookies. It stores them in a proprietary `Cookies.binarycookies` file whose protection is TCC rather than cryptography: the file sits inside the app container, which only apps with Full Disk Access may read. So the gate is a permission the user grants in System Settings, and a denial is reported as exactly that rather than as a generic read failure. Two details the format forces: Timestamps count seconds from 2001-01-01, not the UNIX epoch, so every expiry needs rebasing or cookies import as long expired. The format predates SameSite and carries no equivalent field. Imported cookies are marked Lax, the modern browser default — claiming "none" would widen the scope of every cookie Safari ever set. Safari keeps one jar for the whole app rather than per-profile, so it exposes a single implicit profile, and it has no observable lock file since the jar is written atomically. The parser is covered by tests that build the binary format byte for byte, including a multi-page file — Safari pages its jar, and a single-page reader would silently return only the first slice. That coverage matters because the real file cannot be read on this machine without the very permission the feature asks for; the TCC path itself was verified against the live file, which denies with EPERM and reports `needsFullDiskAccess`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every `nodeBuiltinImport:off` in the import module now says which builtin it covers and why Effect has no equivalent, matching the neighbouring preload and Playwright modules. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… of it `Buffer.subarray` clamps rather than throwing, so every declared structure in the binary format was taken on trust. An overlong page swallowed the following page's bytes and pushed the cursor past the end, dropping every cookie after the boundary from an import that still reported success. A record whose declared size overran its page left its string offsets free to read the next record's bytes as this cookie's value. Pages, records, and string offsets are now bounds-checked against what the file actually contains, and a mismatch fails the read. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Matches the Chromium and Firefox readers: the failure carries which jar it was for, so a Full Disk Access refusal is traceable rather than anonymous. Optional because the parser raises before a path is in hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A Safari import with no Full Disk Access failed with the generic "cookie database could not be read" instead of telling the user to grant access — and no prompt appears, because macOS never prompts for Full Disk Access; the app is added by hand. The denial arrives as EPERM, which Effect tags `Unknown`, not `PermissionDenied` (that is EACCES), so checking the tag alone never matched. The underlying errno is checked too. Verified against the real jar: the reason is now `needsFullDiskAccess`, which the renderer maps to the System Settings instruction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Safari's cookies sit behind Full Disk Access, which no one has granted before their first import — so it is a step in the flow, not a failure. When an import comes back needing it, the wizard shows a screen that says what it's for, links to the right System Settings pane, and — from an "I've turned it on" button — runs the import itself, so the user never restarts from the menu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The step's "Open System Settings" button did nothing: it routed the `x-apple.systempreferences:` deep link through `openExternal`, whose allowlist only passes http(s) and remote-editor schemes, so the link was silently dropped. Adds a dedicated `openSystemSettings(pane)` path instead. The renderer sends a known pane identifier, not a URL, and the main process maps it to an app-fixed deep link and opens it directly — so the general link allowlist stays locked down while this one trusted destination gets through. Uses the post-Ventura Full Disk Access anchor. Also reworks the step's copy to say why the permission is needed and that it can be revoked once the import is done. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two review findings. Safari's parser interpolated a bare IPv6 host into the cookie URL (`http://::1/`), which Electron rejects; hosts with a colon are now bracketed, matching the Chromium reader's cookieScope. And the cookie-database candidate probes accepted any stat-able entry, so a directory squatting on `Network/Cookies` would be picked, fail the SQLite open, and shadow a valid legacy `Cookies` file behind it; every candidate probe now requires a regular file, in the listing, the counts, and the importer alike. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The stale parent.lock test provided only HOME, but Firefox's win32 root hangs off %APPDATA% — so the root was undefined, a `!` hid it, and the fixture wrote a literal `undefined/Profiles/…` tree into the repository instead of the temp directory. The context now carries an APPDATA under the temp home, keeping the fixture (and the assertion) inside the sandbox. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cookieless ones When every profile profiles.ini declared lacked a cookie database, listing returned empty without ever scanning the Profiles directory, so isSourceInstalled reported Firefox absent and hid cookies living in an undeclared profile. The declared path now falls through to the directory scan when it finds nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…d route only TCC to Full Disk Access Two review findings on the Safari reader. The parser validated each declared page but never checked that the declared pages account for the file, so a jar with extra undeclared pages imported "successfully" with those cookies silently missing; it now requires the remainder to be exactly what Safari writes after the pages — nothing, the 8-byte checksum, or checksum plus a length-prefixed property list — and refuses anything else. And `isPermissionDenied` sent ordinary EACCES failures (mode bits, ACLs) to the Full Disk Access grant, which cannot fix them; only TCC's EPERM routes there now, and the TCC test injects a real EPERM instead of relying on chmod's EACCES. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ch other A record offset was only checked against the page's end, so one pointing into the page header/offset table, or back into an already-parsed record, would read those bytes as a fabricated cookie. Offsets must now land past the offset table and records may not overlap any accepted so far. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The listing found Safari's jar with `stat`, which TCC permits without Full Disk Access, so Safari always listed as ready and the wizard opened on the configure step — the permission-first flow, the "still required" recheck and its `resume: "configure"` path could never run, and a refresh could not tell granted from denied. `unavailableReason` now probes the jar by opening it for read; TCC's EPERM reports `needsFullDiskAccess`, while a readable jar or any other failure does not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Stacked on #7261 (
browser-import-linux-windows).Adds Safari cookie import on macOS and a Full Disk Access step. System Settings opens through a dedicated main-process action with a fixed destination. After granting access, the wizard returns to configuration or retries the selected import, depending on where permission was needed.
The Safari reader validates the binary cookie structure, translates Apple-epoch timestamps, and preserves source-path context on parse errors. TCC denial is reported without attempting a bypass.
This layer retains the Windows-user-tested detection contribution #7323: modern/legacy Chromium cookie paths and Firefox’s active Windows lock probe. Access-denied errors are not treated as proof that Firefox is running. Windows Chromium remains intentionally unsupported.
Validation: binary parser fixtures and malformed-record boundaries, filesystem/lock fixtures, fixed System Settings destination, and permission-flow state transitions. Earlier desktop testing by the maintainer is preserved; no new live Safari or Windows GUI test was run in this audit.
Original implementation: Claude Code and external contribution #7323. Review fixes: GPT-5.6 Sol agents, coordinated through Codex.
Note
Add Safari cookie import with Full Disk Access detection and Linux Chromium key fix
BROWSER_IMPORT_SOURCESon macOS and parses Safari'sCookies.binarycookiesformat, converting Apple-epoch timestamps and extracting domain, path, security flags, and SameSite fields in SafariCookies.tsneedsFullDiskAccessunavailable reason; when an EPERM is hit opening the Safari jar, the import wizard in BrowserImportWizard.tsx shows a permission step that can open macOS System Settings and recheck accessopenSystemSettingsIPC channel through the desktop bridge, preload, andElectronShellto open the Privacy & Security Full Disk Access panesecret-tool, so items with the same application attribute but a different schema are ignoredBrowserImportWizardcallers must now provide a requiredopenSystemSettingscallback prop; the keychain-read audit log inBrowserImport.makeis no longer emitted for non-macOS Chromium importsMacroscope summarized d163c94.
Note
Medium Risk
Touches cookie import, macOS TCC/Full Disk Access handling, and new IPC surface; parser mistakes could drop cookies silently, but the diff adds explicit bounds checks and tests for malformed jars.
Overview
Adds Safari as a macOS browser-import source by reading
Cookies.binarycookies, with strict bounds checking on the binary format and mapping TCC EPERM denials to a newneedsFullDiskAccessreason (distinct from ordinary EACCES failures).Pre-flight listing probes Safari jar access via
safariAccessDeniedso the import wizard can land on a Full Disk Access step before configuration. That step can open macOS Privacy & Security → Full Disk Access through a newopenSystemSettings("full-disk-access")path (fixed deep link in main process, pane id over IPC—not arbitrary URLs), wired from preload throughlocalApiinto IntegrationsSettings.Browser import elsewhere: Chromium cookie DB resolution prefers
Network/Cookies(127+) with legacy fallback; Firefox profile discovery skips empty/staleprofiles.inientries and rescans when needed; Windows treats stale Firefoxparent.lockas not running and keeps Chromium forks off win32 (App-Bound Encryption). Keychain audit logging for Chromium imports is limited to darwin.Reviewed by Cursor Bugbot for commit d163c94. Bugbot is set up for automated code reviews on this repo. Configure here.