Skip to content

fix(keychain): bound validation stalls and recover Claude caches - #4089

Open
steipete wants to merge 1 commit into
mainfrom
triage/20260921-claude-creds-3
Open

steipete wants to merge 1 commit into
mainfrom
triage/20260921-claude-creds-3

Conversation

@steipete

Copy link
Copy Markdown
Owner

A stalled native code-signature check can hold the cookie-cache locks and prevent the provider refresh cycle from finishing. Preflight now waits at most two seconds per validation, returns an inconclusive result on timeout, and limits unfinished native checks to four workers. Late results do not authorize reads. The disabled-access startup ordering discussed in #3249 was already corrected by #3258; this change addresses the later process trace of blocked native validation.

Claude also lost a valid credential after a cache write failed: the next refresh cleared the stale-cache marker but did not reconsider memory. It now reuses and persists a fresh, unexpired credential after cleanup succeeds, preserving its profile, owner, history binding, consent, and prompt-policy boundaries. Four identical delegated-refresh cache writes share one helper. Production delta against the starting base: 70 insertions, 74 deletions; net −4 lines.

The original expired-file/fresh-Keychain selection in #3390 is already covered by 79d146d8c273 and d847ebcd9eb1 (#3807); the existing 18-case freshness matrix passes. The remaining external-login discovery in #3395 and Claude-owned partition-ACL replacement in #3798 remain open. Their evidence and the credential precedence rule are documented; this patch does not enable additional foreign-Keychain reads.

Validation used isolated macOS stores and synthetic credentials with real Keychain access suppressed. In one before/after invocation, the two regression bodies failed on the original production files at 491ae688a89a (2 tests, 3 expected issues), then the candidate passed 579 tests in 65 suites. The original initializer shape was used only to compile the unrelated coalescing test against the baseline.

env -u CODEXBAR_ALLOW_TEST_KEYCHAIN_ACCESS CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS=1 \
  swift test --build-system native --disable-index-store --jobs 2 -debug-info-format none --no-parallel \
  --filter 'ClaudeOAuthBackgroundCacheRecoveryTests|KeychainAccessValidationMemoTests.*stalled'

Baseline: Test run with 2 tests in 2 suites failed ... with 3 issues. The rejected-write case returned notFound; the stalled validator returned success after five seconds instead of timing out.

env -u CODEXBAR_ALLOW_TEST_KEYCHAIN_ACCESS CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS=1 \
  swift test --build-system native --disable-index-store --jobs 2 -debug-info-format none --no-parallel \
  --filter 'KeychainPromptSafetyAuditTests|KeychainNoUIQueryTests|KeychainAccess|KeychainCache|ClaudeOAuth|ClaudeCredential|ClaudeSecurityCLI|ClaudeActiveAccountIdentityInvalidationTests|ClaudeProvider|ClaudeLogin|ClaudeCLIBackgroundAvailabilityTests|MenuRefresh|SettingsStoreKeychainPreferenceTests|ClaudeWebRecoveryMenuTests|MenuOpenRefreshPlanTests|KeychainStringStoreTests|ProviderArchitectureGatekeeperTests'
make check

Candidate: Test run with 579 tests in 65 suites passed after 40.902 seconds. make check passed: 0/2671 files require formatting and Found 0 violations, 0 serious in 2670 files. Independent Codex P2 review found no actionable findings. No app relaunch, account change, or live credential read was performed.

Fixes #3249
Closes #3390
Refs #3395
Refs #3798

Bound native signature checks while keeping stalled work within four slots. Recover and repersist fresh Claude memory credentials after rejected cache writes are cleaned up, preserving credential ownership and prompt-policy boundaries.

Refs #3249, #3390, #3395, #3798.
@clawsweeper

clawsweeper Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added P1 Urgent regression or broken agent/channel workflow affecting real users now. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 28, 2026
@clawsweeper

clawsweeper Bot commented Sep 28, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed September 27, 2026, 11:40 PM ET / September 28, 2026, 03:40 UTC.

ClawSweeper review

What this changes

The branch limits how long Keychain signature validation can delay refreshes and lets Claude reuse and persist a valid in-memory credential after cache cleanup.

Merge readiness

⛔ Blocked before merge - 7 items remain

Keep this PR open. Its fixes remain useful and are absent from current main, but the new cache write can preserve a Claude credential after the user revokes consent to read it.

Priority: P1
Reviewed head: ab925af752533b3da733518522066484aa30d6fb

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) The focused validation supports the intended fixes, but a source-visible consent-revocation defect blocks merge.
Proof confidence 🦪 silver shellfish (2/6) Needs stronger real behavior proof before merge: Authority-chain proof required: the PR reports synthetic, Keychain-suppressed tests for stalled validation and allowed Claude recovery, but no final cache-write and OAuth-use observation for consent revoked between memory selection and persistence. An injected revocation harness should exercise the production routing and cache owner without live credentials. The existing CacheEntry format is reused, so no stored-data migration is required. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🧂 unranked krab (1/6) Security review found an item that needs attention.

Verification

Check Result Evidence
Real behavior Needs proof Needs stronger real behavior proof before merge: Authority-chain proof required: the PR reports synthetic, Keychain-suppressed tests for stalled validation and allowed Claude recovery, but no final cache-write and OAuth-use observation for consent revoked between memory selection and persistence. An injected revocation harness should exercise the production routing and cache owner without live credentials. The existing CacheEntry format is reused, so no stored-data migration is required. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 7 items Introduced cache write: After selecting a memory credential, the PR persists it through saveCredentialsToCache without carrying the consent epoch under which it was obtained.
Revocation contract: Disabling direct Claude Keychain reads advances a revocation marker and clears the active memory and persistent caches; the settings comment explicitly says copied credentials must be retired.
Persistence boundary: The memory-record check verifies profile, timestamp, expiry, and pending cleanup, but no consent epoch. A newly written CacheEntry takes the current revocation marker, so an older memory credential can be stamped as current after revocation.
Findings 1 actionable finding [P1] Bind cache repersistence to the credential's consent epoch
Security Needs attention Revoked Claude credential can be repersisted: The memory record has no acquisition epoch, while the new persistent entry receives the current marker; revocation between selection and save can therefore make an old credential usable again.

How this fits together

CodexBar checks Keychain access while refreshing provider usage. Claude credential selection then reads its cache, memory, or other permitted sources before requesting usage.

flowchart LR
A[Provider refresh] --> B[Keychain preflight]
B --> C{Validation completes?}
C -->|Yes| D[Cache lookup]
C -->|Timeout| E[Inconclusive access]
D --> F[Claude memory recovery]
E --> F
F --> G[Cache write]
G --> H[Usage result]
Loading

Before merge

  • Add real behavior proof - Needs stronger real behavior proof before merge: Authority-chain proof required: the PR reports synthetic, Keychain-suppressed tests for stalled validation and allowed Claude recovery, but no final cache-write and OAuth-use observation for consent revoked between memory selection and persistence. An injected revocation harness should exercise the production routing and cache owner without live credentials. The existing CacheEntry format is reused, so no stored-data migration is required. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Bind cache repersistence to the credential's consent epoch (P1) - A refresh can select a Claude Keychain credential from memory here, then the user can revoke direct-read consent before this new save runs. Revocation clears the caches, but the save creates a fresh entry stamped with the new marker, so a later refresh can use the revoked credential. Carry its acquisition epoch into the write and reject stale records before persistence.
  • Resolve security concern: Revoked Claude credential can be repersisted - The memory record has no acquisition epoch, while the new persistent entry receives the current marker; revocation between selection and save can therefore make an old credential usable again.
  • Resolve merge risk (P1) - An in-flight refresh can write a Claude credential obtained before direct-read consent was revoked into a new cache entry stamped with the current revocation marker, leaving it available to later refreshes.
  • Complete next step (P2) - Prevent repersistence across consent revocation and provide final-effect proof for the revoked-credential case before merge.
  • Improve patch quality - Prevent a memory credential from being persisted under a newer consent epoch.
  • Improve patch quality - Add final-effect proof that revocation prevents both the cache write and subsequent OAuth use, using isolated stores and redacted output.

Findings

  • [P1] Bind cache repersistence to the credential's consent epoch — Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift:382-387
  • [high] Revoked Claude credential can be repersisted — Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift:382
Agent review details

Security

Needs attention: The new Claude cache write can outlive revocation of the consent that authorized its source credential.

Review metrics

Metric Value Why it matters
Code delta production +70/−74 lines; tests +103/−38 lines Production scope is contained, while the added tests cover recovery and timeouts but not revocation during the new write.

Merge-risk options

Maintainer options:

  1. Guard repersistence at the consent boundary (recommended)
    Preserve the credential's acquisition epoch and prove that revocation prevents its cache write and subsequent OAuth use before merging.

Technical review

Best possible solution:

Bind each in-memory Claude credential to its acquisition consent epoch and commit cache writes only while that epoch remains authorized; retain the bounded validation behavior.

Do we have a high-confidence way to reproduce the issue?

No live PR-path reproduction was run. Source establishes an interleaving: select the memory credential, revoke consent and clear caches, then let the introduced save persist that old credential.

Is this the best way to solve the issue?

No, not yet. Bounded validation and memory recovery fit the reported failures, but repersistence must honor the consent epoch at the final cache write.

Full review comments:

  • [P1] Bind cache repersistence to the credential's consent epoch — Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift:382-387
    A refresh can select a Claude Keychain credential from memory here, then the user can revoke direct-read consent before this new save runs. Revocation clears the caches, but the save creates a fresh entry stamped with the new marker, so a later refresh can use the revoked credential. Carry its acquisition epoch into the write and reject stale records before persistence.
    Confidence: 0.88

Overall correctness: patch is incorrect
Overall confidence: 0.88

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 579f68406855.

Labels

Label changes:

  • add P1: The PR addresses a reported refresh stall, but its introduced cache write needs an authorization-boundary repair before merge.
  • add merge-risk: 🚨 security-boundary: A copied Claude credential can be persisted after the user revokes consent for the direct read that obtained it.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: Authority-chain proof required: the PR reports synthetic, Keychain-suppressed tests for stalled validation and allowed Claude recovery, but no final cache-write and OAuth-use observation for consent revoked between memory selection and persistence. An injected revocation harness should exercise the production routing and cache owner without live credentials. The existing CacheEntry format is reused, so no stored-data migration is required. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P1: The PR addresses a reported refresh stall, but its introduced cache write needs an authorization-boundary repair before merge.
  • merge-risk: 🚨 security-boundary: A copied Claude credential can be persisted after the user revokes consent for the direct read that obtained it.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: Authority-chain proof required: the PR reports synthetic, Keychain-suppressed tests for stalled validation and allowed Claude recovery, but no final cache-write and OAuth-use observation for consent revoked between memory selection and persistence. An injected revocation harness should exercise the production routing and cache owner without live credentials. The existing CacheEntry format is reused, so no stored-data migration is required. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

Security concerns:

  • [high] Revoked Claude credential can be repersisted — Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift:382
    The memory record has no acquisition epoch, while the new persistent entry receives the current marker; revocation between selection and save can therefore make an old credential usable again.
    Confidence: 0.88

Acceptance criteria:

  • [P1] env -u CODEXBAR_ALLOW_TEST_KEYCHAIN_ACCESS CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS=1 swift test --no-parallel --filter 'ClaudeOAuthDirectKeychainReadConsentTests|ClaudeOAuthBackgroundCacheRecoveryTests'.
  • [P1] make test.
  • [P1] make check.

What I checked:

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Lam,Yiu Fung: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

1 participant