Skip to content

fix(claude): handle hex-encoded keychain credentials#48

Merged
robinebers merged 2 commits into
mainfrom
fix/claude-keychain-credentials
Feb 3, 2026
Merged

fix(claude): handle hex-encoded keychain credentials#48
robinebers merged 2 commits into
mainfrom
fix/claude-keychain-credentials

Conversation

@robinebers
Copy link
Copy Markdown
Owner

@robinebers robinebers commented Feb 3, 2026

Fixes credential loading when macOS keychain returns hex-encoded UTF-8 bytes instead of plain JSON.

TLDR: Some macOS keychain items are returned by security ... -w as hex-encoded strings (e.g., 7b0a... for {\n...). This PR adds support for decoding them.

Changes

  • Add tryParseCredentialJSON() helper that attempts plain JSON parse first, then falls back to hex decoding
  • Apply the helper to both file and keychain credential loading paths
  • Add test for hex-encoded keychain credentials

Made with Cursor


Note

Low Risk
Low risk: changes are isolated to credential parsing in the Claude plugin, adding a safe fallback decoder plus tests; main risk is edge-case parsing behavior for unexpected keychain/file contents.

Overview
Fixes Claude credential loading when stored data isn’t plain JSON by adding tryParseCredentialJSON() with a hex-to-UTF8 decode fallback (using TextDecoder when available, otherwise a minimal UTF-8 decoder).

loadCredentials() now uses this parser for both the credentials file and keychain paths, and tests cover file-corruption fallback to keychain plus hex-encoded (including non-ASCII) keychain values.

Written by Cursor Bugbot for commit 159cffa. This will update automatically on new commits. Configure here.


Summary by cubic

Fixes credential parsing in the Claude plugin when macOS Keychain returns hex-encoded JSON, including non-ASCII, so OAuth tokens load reliably from both file and keychain. Prevents auth failures when security ... -w outputs hex (e.g., "7b0a...").

  • Bug Fixes
    • Added tryParseCredentialJSON that parses JSON or falls back to hex→UTF-8 decoding (supports 0x-prefixed hex and non-ASCII).
    • Applied the helper to both file and keychain paths; added tests for hex-encoded values, non-ASCII, and corrupt-file fallback.

Written for commit 159cffa. Summary will update on new commits.

- Added a new function to parse credentials from hex-encoded JSON format, improving compatibility with macOS keychain items.
- Updated the credential loading logic to utilize the new parsing function for both file and keychain sources.
- Added a test case to verify the correct handling of hex-encoded JSON credentials.
@macroscopeapp
Copy link
Copy Markdown

macroscopeapp Bot commented Feb 3, 2026

Handle hex-encoded UTF-8 keychain credentials and parse them via plugins/claude/plugin.js::tryParseCredentialJSON to load claudeAiOauth.accessToken

Add utf8DecodeBytes and tryParseCredentialJSON, and update credential loading to parse hex-encoded UTF-8 from file and keychain in plugin.js; add tests in plugin.test.js.

📍Where to Start

Start with the loadCredentials handler and its use of tryParseCredentialJSON in plugin.js.


Macroscope summarized 159cffa.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f5d017ef9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/claude/plugin.js Outdated
Comment on lines 40 to 43
const text = ctx.host.fs.readText(CRED_FILE)
const parsed = JSON.parse(text)
const parsed = tryParseCredentialJSON(text)
if (!parsed) return null
const oauth = parsed.claudeAiOauth
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow keychain fallback when file parse fails

If the credentials file exists but contains malformed JSON (e.g., partial write), tryParseCredentialJSON returns null and this code returns immediately, so the keychain fallback is never attempted. Before this change, a parse error in the file path would drop into the keychain branch, so users with a corrupt file but valid keychain entry now get a hard "Not logged in" error. Consider only returning null when a valid parsed file is present but missing tokens, and otherwise continue to keychain.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Comment thread plugins/claude/plugin.js Outdated
Comment thread plugins/claude/plugin.js
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="plugins/claude/plugin.js">

<violation number="1" location="plugins/claude/plugin.js:28">
P3: The hex decoding uses `String.fromCharCode` per byte, which treats input as Latin-1 encoding rather than UTF-8. Multi-byte UTF-8 sequences (like 'é' = 0xC3 0xA9) would be incorrectly decoded as two Latin-1 characters. Consider using `TextDecoder` for proper UTF-8 decoding: `new TextDecoder().decode(new Uint8Array(hex.match(/.{2}/g).map(b => parseInt(b, 16))))`.</violation>

<violation number="2" location="plugins/claude/plugin.js:42">
P1: Early `return null` breaks the keychain fallback. If the credential file exists but can't be parsed, this now returns null immediately instead of falling through to try the keychain. Previously, `JSON.parse` would throw and the empty `catch` block would allow fallthrough to keychain.

Wrap the oauth check in a conditional instead to preserve fallback behavior:</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread plugins/claude/plugin.js Outdated
Comment thread plugins/claude/plugin.js Outdated
- Added a new function to decode UTF-8 from hex-encoded byte arrays, enhancing the handling of non-ASCII characters in credentials.
- Updated the credential loading logic to utilize the new decoding function for improved robustness.
- Added tests to verify the correct decoding of hex-encoded JSON, including scenarios with corrupt files and non-ASCII characters.
@robinebers robinebers merged commit 5f14123 into main Feb 3, 2026
3 checks 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