Files
orca/src/shared/linear
Neil 5fa290fa50 feat(secrets): warn in Settings when a credential is stored unencrypted (#24048)
* feat(secrets): warn in Settings when a credential is stored unencrypted

When no OS keyring is usable, the MiniMax stores write the credential as a
plaintext envelope and say so with a console.warn nobody reads. The users this
affects are exactly the ones who never see a main-process log, so in practice
they were told nothing (#21827).

Report it where the credential is managed instead. Each store gains a
protection reader, the status IPC carries it, and Settings renders a warning
next to the credential it applies to.

Keyed on the stored bytes, not isEncryptionAvailable(): a credential saved
before a keyring existed stays plaintext until it is saved again, so reporting
current capability would call it protected while the file says otherwise. The
readers parse the envelope kind without decrypting, so opening Settings cannot
provoke a keychain prompt.

The console.warn stays. It carries no secret material, and it is still the only
signal on a headless host with no Settings window.

* feat(secrets): extend the unsealed-credential warning to every affected store

The speech key, Linear tokens, Jira tokens and the Bitbucket credential have
the same plaintext fallback the MiniMax stores do, and the same console-only
warning nobody reads.

Add a shared `readCredentialFileProtection` for the four stores that write bare
ciphertext with no envelope, classifying with the same printable-UTF-8 test
`readStoredCredentialToken` already uses — so the reporter cannot drift into
disagreeing with the reader about the same bytes.

Linear and Jira report across every stored workspace/site rather than the
active one: sealing is a host-wide property, so a second workspace stored while
the keyring was missing is exposed even when the active one is sealed. Both
fields are optional, so an older remote host that omits them reads as unknown
rather than as sealed. Bitbucket reports null for env-supplied auth, where Orca
stores nothing and has no claim to make.

Also fixes the credential-connection test double, whose identity-function
`encryptString` wrote a readable token — faithful enough for a round-trip
assertion, but it made the suite assert that a sealed credential was exposed.

* chore(i18n): extract the unsealed-credential notice strings

CI's localization-extraction gate requires every translate() key to exist in
the primary catalog. Inserted in place rather than re-sorting the file, which
is not fully sorted and would have produced a 17k-line diff.

* test(web): pin the null protection fields on the desktop-only MiniMax bridge

The web bridge reports no protection because it stores nothing; the shape
assertions had to move with it.
2026-09-30 02:13:03 -07:00
..
…