[Bug]: macOS browser import reports inaccessible encryption keys as missing
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 28/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- macos, rust, typescript
調査の方向性
Start with readKeychainSecret and the falsy-secret branch in apps/desktop/src/preview/BrowserImport/ChromiumKeys.ts, then the denial fixture in ChromiumKeys.test.ts. The root cause sits in the pinned @napi-rs/keyring 1.3.0 dependency (pnpm-lock.yaml), which converts native read errors to absent values, so the fix likely needs a dependency decision as well as code. Done means an access failure yields needsKeychainApproval, a missing key still yields keychainItemMissing, and a successful read is unchanged.
索引モデルが issue の本文から書いたものです。
説明
Summary
When a macOS browser encryption-key read fails, browser import reports that the key does not exist and tells the user to sign in to the source browser again. An existing key that could not be read should retain its access failure so the user receives the appropriate approval or keyring error. A source trace through the exact pinned native binding establishes that it discards the failure before the application's error handler can see it; no real Keychain access was performed.
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Credentials, private paths, and sensitive data were excluded from this report.
Area
apps/desktop
Observed behavior
Source-only finding: the supported Chromium browser-import path on macOS calls AsyncEntry.getPassword(), but the resolved @napi-rs/keyring 1.3.0 implementation converts every native read error into an absent optional value. For an existing key whose platform read fails with an access error, T3 therefore takes the falsy-secret branch and produces keychainItemMissing, rather than taking the catch branch that produces needsKeychainApproval. The import error preserves that reason through ChromiumCookieReadError and BrowserImportFailedError, selecting the missing-key instruction to sign in to the source browser again.
Reproduction
- Inspect the desktop dependency resolution at the stated commit: pnpm-lock.yaml resolves @napi-rs/keyring to 1.3.0.
- Follow Settings → Browser → Browser profiles → Add profile → Import from a Chromium browser into BrowserImport.importCookies, readChromiumCookies, resolveChromiumKeys with platform darwin, and readKeychainSecret.
- Follow the published package's npm gitHead to PasswordTask.compute. For a platform result Err(NoStorageAccess), the .ok() conversion yields None and the N-API task resolves successfully with an absent value; it does not reject.
- Follow that value into readKeychainSecret: the catch handler is bypassed and the falsy-secret branch emits keychainItemMissing. Compare this with the rejected-promise denial fixture in ChromiumKeys.test.ts, which never models the pinned wrapper's error-erasing behavior.
These are source-inspection steps, not a native reproduction. The conversion is unconditional for any native read error. NOT_ENOUGH_INFO: the frequency of macOS users hitting native access failures was not measured; a disposable macOS account with a synthetic item and denied access would establish a live incident without exposing real credentials.
Reproduction script: NOT_ENOUGH_INFO — no native Keychain lookup was run, and there is no public dependency hook for injecting a native credential-store result. This report supplies the exact source path; a disposable macOS account or a native binding fixture would permit an executable end-to-end check.
Expected behavior
An inaccessible existing browser key must remain distinguishable from an absent key. T3 already represents these outcomes separately: its rejected-read handler produces needsKeychainApproval, and its missing-result handler produces keychainItemMissing in apps/desktop/src/preview/BrowserImport/ChromiumKeys.ts#L62-L75. The distinct user guidance is defined in packages/contracts/src/browserImport.ts#L79-L94, and apps/desktop/src/preview/BrowserImport/ChromiumKeys.test.ts#L114-L123 expects an access denial to retain the approval reason and original cause.
The pinned AsyncEntry declaration also documents missing and ambiguous results separately. The dependency's later released src/result.rs#L3-L16 preserves this distinction, mapping only NoEntry to absence and propagating other errors.
Impact and scope
Form impact: Minor bug or occasional failure.
Impact: P2 - Native read failures lose their cause and select an incorrect recovery instruction during macOS Chromium session import. The operation fails safely without importing cookies, but users are sent to source-browser sign-in instead of the actual access problem.
The source path is shared by the supported macOS Chromium browsers. Successful reads and genuinely missing keys are unaffected by this finding. Linux uses the separate libsecret helper and is outside this defect. NOT_ENOUGH_INFO: no native prompt was denied in this investigation, and no practical recovery procedure was exercised; those require a disposable macOS account with synthetic credentials.
Version
T3 Code commit 59e64eef881632a23f89c4138bcf37f23d92bf93. The desktop pnpm-lock.yaml#L434-L436 resolves @napi-rs/keyring 1.3.0. Its published npm metadata identifies gitHead e46be75c3ba8d5fde6b88a17c6153b87ffe4b946. No keyring patch exists in this checkout's patches directory. No dependency or installed application was changed.
Runtime and platform
Affects the desktop macOS Chromium import branch. Inspection ran on macOS arm64. No native keyring method, browser-secret executable, real credential, or Keychain item was accessed. NOT_ENOUGH_INFO: no affected packaged application's macOS version or architecture-specific native run was established; this is a source-only report against the pinned dependency and application code.
Interface
UI: desktop browser profile session import, backed by the @napi-rs/keyring AsyncEntry library API.
Earliest failure stage
Integration or transport: the native binding erases the credential-store read error before returning to TypeScript.
Relevant output or evidence
The exact dependency implementation is src/async_entry.rs#L247-L258. Its PasswordTask.compute contains:
fn compute(&mut self) -> Result<Self::Output> {
Ok(self.inner.get_password().ok())
}
Thus Err(NoStorageAccess), Err(PlatformFailure), and Err(NoEntry) all become a successful absent optional result. The subsequent T3 read mapping cannot recover the discarded cause. ChromiumCookies.ts#L280-L289 and BrowserImport.ts#L316-L323 preserve the resulting missing-key reason to the import boundary.
The upstream Apple backend's keychain.rs#L360-L373 distinguishes item-not-found from access errors. The wrapper removes that distinction. Evidence provenance: static. Local verification: static-proof. Reproduction completeness: incomplete for a native macOS incident; complete for the conditional source path from a native read error to the incorrect application reason.
Expected outcome or recovery
For a native access-denied result, import reports an access/approval failure rather than claiming the key is absent. A genuinely missing key retains missing-key guidance, and a successful read still derives the same cookie key. A check of the released integration must exercise those three native outcomes, not just a JavaScript promise mock that rejects even though the pinned native implementation resolves.
Additional context
- https://github.com/Brooooooklyn/keyring-node/pull/136 — reports and fixes this exact dependency error-erasure behavior; the discussion identifies its release in 2.0.0. The latest published 2.1.2 source also preserves non-missing errors, while this T3 checkout still resolves 1.3.0.
- https://github.com/pingdotgg/t3code/pull/23157 — switched this T3 path to AsyncEntry and added resolved-undefined and rejected-denial mocks; its stated untested cases include denial on the async branch. Responsiveness is a separate contract from preserving native failure reasons.
- https://github.com/pingdotgg/t3code/issues/29234 — concerns Linux KWallet attribute lookup rather than macOS native read-error translation; its remedy does not cover this path.
- 主要言語
- TypeScript
- スター
- 24.8k
- フォーク
- 6.4k
- 平均マージ
- 59分
- マージ済み PR(30日)
- 418
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
pingdotgg/t3code のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
メンテナーはふだん 1 日以内に返信
-
via-triage
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 半日 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
Question card options show LaTeX as raw source, though the question text renders it対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信
-
[Bug]: Settings inheritance popover shows raw slugs for branch naming, default permissions and response streaming対応中かも @juliusmarminge が 1 日前に担当しました。 オープンvouch:denounced
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
メンテナーはふだん 1 日以内に返信
pingdotgg/t3code の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
nikoheikkila/nikoheikkila.fi#1131 ·
メンテナーはふだん 1 日以内に返信
-
Resources: Update work of urumqi対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
railmapgen/rmp-gallery#4176 ·
メンテナーはふだん 2 日以内に返信
-
GraphQL custom headers duplicate differently cased default headers対応中かも @yxr-2025 が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
elie222/rakazo#4625 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信
-
[New Project]: Pacaオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
github/forgoodfirstissue#574 ·