Skip to content

Read the Claude active-organization cookie in process - #39

Merged
euforic merged 1 commit into
euforic/wonderful-mcnulty-8b981efrom
euforic/sad-perlman-38a702
Oct 30, 2026
Merged

euforic merged 1 commit into
euforic/wonderful-mcnulty-8b981efrom
euforic/sad-perlman-38a702

Conversation

@euforic

@euforic euforic commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Stacked on #38. Review and merge that first. This PR's diff is only the commit on top of it.

Summary

ClaudeCredentialReader.activeOrganization(userDataURL:key:) was the last place that launched /usr/bin/sqlite3 to read a provider database during a refresh. It now reads the lastActiveOrg cookie from Cookies or Network/Cookies with the in-process SQLiteReadOnly.query reader added in #38.

  • SQLiteReadOnly.query gains an under root: URL? = nil parameter that it passes to databaseArgument. The cookie lookup passes the profile's user-data root, so the containment check against the profile root stays in place. Existing callers are unchanged.
  • The query now returns host_key, CAST(value AS TEXT) and CAST(encrypted_value AS BLOB) as separate typed columns instead of the old hex-and-| string. A non-empty plain value is still used first.
  • Decryption (v10 AES-CBC), the SHA-256 host-key prefix check and the UUID check are unchanged.
  • The 2 s timeout and 4 KiB output limit are unchanged, now with maximumRows: 1. Any query error still gives nil for that database.
  • The Data(hexString:) helper was no longer used, so it is removed.
  • activeOrganization goes from private to internal so tests can call it without a Keychain.
  • Docs: added a short paragraph to docs/resource-usage-readers.md and updated one sentence in docs/security.md.

Tests

All tests use real SQLite files created through the SQLite C API and real AES encryption, with no mocks.

  • ClaudeCredentialReaderTests:
    • Plain cookie: the newest row wins, and other hosts and cookie names are ignored.
    • Encrypted cookie in Network/Cookies: it decrypts, and a value encrypted for a different host is rejected.
    • A Network folder symlinked to another profile's folder is rejected, while that other profile still reads its own cookie.
    • A missing cookies table gives nil, and the lookup still moves on to Network/Cookies.
  • SQLiteReadOnlyTests: the new under: parameter on query rejects a symlinked parent from another profile. The same query without a root follows the link.
  • Mutation check: with under: userDataURL removed, the symlink test fails.
  • swift test passes: 342 core tests and 63 app tests, with only the usual opt-in tests skipped.

🤖 Generated with Claude Code

The lastActiveOrg lookup was the last provider-database sqlite3 launch on
the refresh path. It now uses SQLiteReadOnly.query, which gains an
`under:` root forwarded to databaseArgument so the profile-root containment
check is kept. The query returns host_key, the plain value, and the
encrypted value as typed text and blob columns instead of a hex-and-pipe
string; decryption, host-hash and UUID validation are unchanged, as are
the 2 s timeout and 4 KiB budget, now with a one-row limit. Any query
failure still yields no organization.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@euforic
euforic merged commit d1557a0 into euforic/wonderful-mcnulty-8b981e Oct 30, 2026
1 check passed
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