Skip to content

feat: add a Firefox cookie backend, the first that works off macOS - #18

Merged
samlehoy merged 1 commit into
masterfrom
feat/firefox-cookie-backend
Aug 19, 2026
Merged

samlehoy merged 1 commit into
masterfrom
feat/firefox-cookie-backend

Conversation

@samlehoy

Copy link
Copy Markdown
Owner

Auto-fetch has been macOS-only because both backends lean on the login Keychain. Firefox needs no keychain at all — cookies.sqlite stores values in plaintext on every OS — so one backend covers Windows, macOS and Linux, with no new dependency (node:sqlite was already imported for the Chromium DB).

Why Firefox rather than Chromium-on-Windows

Chromium on Windows is closed, not pending. Since Chrome 127 (July 2024) App-Bound Encryption stores cookies as v20 blobs whose key is wrapped by User-DPAPI and then SYSTEM-DPAPI, so only a SYSTEM process or elevation_service.exe can unwrap it — the feature exists precisely to stop what this module does. Every published bypass is COM hijacking or injection into the elevation service, which is malware behaviour and would get plx flagged as an infostealer.

A related correction: the old note claimed Firefox cookies were key4.db-encrypted. They aren't — key4.db protects saved logins. That mistake is what made the cheapest backend look like the hardest one.

Two details that carry the implementation

Profile ranking by mtime, not profiles.ini. Candidate cookies.sqlite files are sorted newest-written first. Someone with several profiles is logged in on the one they last wrote to, which is what an mtime sort answers directly — and it avoids an INI parser. The ordering test names its fixtures so alphabetical order is the opposite of mtime order, so a readdir that got it right by luck still fails.

WAL handling. Firefox holds the DB open in WAL mode, and on Windows that lock is enforced — reading in place fails outright. The DB is copied to a temp dir along with its -wal sidecar (which holds cookies set this session, not yet checkpointed) and the copy is opened writable, because replaying a WAL requires write access.

Blast radius

macOS behaviour is unchanged. The keychain-backed backends still run first, in their original order; Firefox only fills what they left empty. The platform() !== 'darwin' early return became a per-backend gate rather than a gate on the whole function.

Verification

124 tests pass (up from 116). The new ones run against real cookies.sqlite files built by node:sqlite rather than stubs, covering the schema read, host ranking (including the .google.com vs .youtube.com collision that silently breaks a YouTube Music session), profile ordering, the flat-layout fallback, and the not-a-database path.

Not verified live: Firefox was not installed on the development machine, so this has never run against a genuine logged-in profile. That gap is recorded in docs/CONTINUATION.md rather than papered over. Worth one manual run before anyone leans on it.

🤖 Generated with Claude Code

Auto-fetch was macOS-only because both backends lean on the login Keychain.
Firefox needs no keychain at all: cookies.sqlite stores values in plaintext on
every OS, so one backend covers Windows, macOS and Linux and adds no dependency
— node:sqlite was already imported for the Chromium DB.

This is also the only route to auto-fetch on Windows. Chromium there is closed,
not pending: since Chrome 127 App-Bound Encryption wraps the cookie key in
SYSTEM-DPAPI, and every published bypass is COM hijacking or injection into the
elevation service.

Two things the backend has to get right:

- Profiles are ranked by cookies.sqlite mtime, newest first, instead of parsing
  profiles.ini. Someone with several profiles is logged in on the one they last
  wrote to, which is what an mtime sort answers directly.
- Firefox holds the DB open in WAL mode and on Windows that lock is enforced, so
  the DB is copied to a temp dir with its -wal sidecar and the copy is opened
  writable — replaying a WAL needs write access, and the sidecar holds cookies
  set this session that are not checkpointed yet.

macOS behaviour is unchanged: the keychain-backed backends still run first and
in their original order, with Firefox filling only what they left empty.

Tested against real cookies.sqlite files built by node:sqlite — host ranking,
profile ordering, and the failure paths. Firefox was not installed on the
development machine, so this has not run against a genuine logged-in profile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
plx 6dd9237 Commit Preview URL

Branch Preview URL
Aug 19 2026, 08:32 AM

@samlehoy
samlehoy merged commit 1a4acfd into master Aug 19, 2026
7 checks passed
@samlehoy
samlehoy deleted the feat/firefox-cookie-backend branch August 19, 2026 08:36
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