fix(browser): keep cookie-import warnings readable from newer hosts

The import summary is cast, not decoded, coming off the runtime RPC wire, so a
newer host can publish a warning code or undecryptable reason this build has
never seen. Both switches then matched nothing and returned undefined, which
rendered as a blank toast.warning(undefined).

#14683 replaced the absorbing default: arm with case 'unknown' to satisfy
switch-exhaustiveness-check, which forbids default: on an exhaustive switch.
Guard before each switch instead: narrow the wire value against the handled set
and route anything else to a generic message. The handled sets are keyed by the
unions themselves, so a fourth member still fails typecheck here.
This commit is contained in:
Brennan Benson
2026-08-16 19:59:28 -07:00
parent b6d5972ec4
commit d3b3bfe64a
3 changed files with 84 additions and 2 deletions
+3 -1
View File
@@ -788,7 +788,9 @@
"undecryptableAppBoundMixed": "Orca cannot decrypt {{value0}} of this browser's cookies because they use app-bound encryption; {{value1}} more could not be decrypted for another reason. You can import cookies from a file using “From File…”.",
"undecryptableKeyring": "{{value0}} cookies could not be decrypted because the system keyring was unavailable. Unlock your login keyring (or install a Secret Service provider such as gnome-keyring) and import again.",
"undecryptableKeyringMixed": "{{value0}} cookies could not be decrypted because the system keyring was unavailable; {{value1}} more could not be decrypted for another reason. Unlock your login keyring (or install a Secret Service provider such as gnome-keyring) and import again.",
"undecryptableUnknown": "{{value0}} cookies could not be decrypted and were skipped. Close the source browser completely and try the import again."
"undecryptableUnknown": "{{value0}} cookies could not be decrypted and were skipped. Close the source browser completely and try the import again.",
"unrecognizedWarning": "The cookie import finished with a warning this version of Orca does not recognize. Update Orca to see the details, then check this profile before relying on its cookies.",
"undecryptableUnrecognizedReason": "{{value0}} cookies could not be decrypted and were skipped for a reason this version of Orca does not recognize. Update Orca to see the details, then try the import again."
}
}
}
@@ -101,6 +101,46 @@ describe('emitBrowserCookieImportToast', () => {
expect(warningToastMock).not.toHaveBeenCalled()
})
// Why: the summary crosses the runtime RPC wire and is cast, not decoded, so a newer host can
// publish a reason/code this client build has never heard of (#14683 follow-up).
it('still warns when a newer host sends an undeclared undecryptable reason', () => {
emitBrowserCookieImportToast(
{
...summary,
warning: {
code: 'cookies-undecryptable',
failedCookies: 3,
reason: 'hardware-token-required'
} as unknown as BrowserCookieImportSummary['warning']
},
'Imported 0 cookies.',
'Remote Linux'
)
const message = warningToastMock.mock.calls[0]?.[0]
expect(typeof message).toBe('string')
expect(message).not.toBe('')
expect(message).toContain('3')
})
it('still warns when a newer host sends an undeclared warning code', () => {
emitBrowserCookieImportToast(
{
...summary,
warning: {
code: 'profile-locked',
failedCookies: 3
} as unknown as BrowserCookieImportSummary['warning']
},
'Imported 0 cookies.',
'Remote Linux'
)
const message = warningToastMock.mock.calls[0]?.[0]
expect(typeof message).toBe('string')
expect(message).not.toBe('')
})
it('keeps both applicable warnings when restart fallback is unavailable', () => {
emitBrowserCookieImportToast(
{
@@ -3,8 +3,39 @@ import type { BrowserCookieImportSummary } from '../../../shared/browser-workspa
import { translate } from '@/i18n/i18n'
type CookieImportWarning = NonNullable<BrowserCookieImportSummary['warning']>
type CookieImportWarningCode = CookieImportWarning['code']
type UndecryptableReason = Extract<CookieImportWarning, { code: 'cookies-undecryptable' }>['reason']
// Why: the summary is cast, not decoded, on the way off the runtime RPC wire, so a newer host can
// send a code/reason this build has never heard of. The Record keys are the union itself, so a new
// member fails typecheck here instead of silently falling out of the switch (#14683 follow-up).
const HANDLED_WARNING_CODES: Record<CookieImportWarningCode, true> = {
'restart-fallback-unavailable': true,
'cookies-undecryptable': true
}
const HANDLED_UNDECRYPTABLE_REASONS: Record<UndecryptableReason, true> = {
'app-bound-encryption': true,
'linux-keyring-unavailable': true,
unknown: true
}
function isHandledWarningCode(code: string): code is CookieImportWarningCode {
return Object.hasOwn(HANDLED_WARNING_CODES, code)
}
function isHandledUndecryptableReason(reason: string): reason is UndecryptableReason {
return Object.hasOwn(HANDLED_UNDECRYPTABLE_REASONS, reason)
}
function formatCookieImportWarning(warning: CookieImportWarning): string {
const code: string = warning.code
if (!isHandledWarningCode(code)) {
return translate(
'auto.lib.browser.cookie.import.toast.unrecognizedWarning',
'The cookie import finished with a warning this version of Orca does not recognize. Update Orca to see the details, then check this profile before relying on its cookies.'
)
}
switch (warning.code) {
case 'restart-fallback-unavailable':
return warning.loadedCookies === 0
@@ -21,7 +52,15 @@ function formatCookieImportWarning(warning: CookieImportWarning): string {
value1: warning.loadedCookies + warning.failedCookies
}
)
case 'cookies-undecryptable':
case 'cookies-undecryptable': {
const reason: string = warning.reason
if (!isHandledUndecryptableReason(reason)) {
return translate(
'auto.lib.browser.cookie.import.toast.undecryptableUnrecognizedReason',
'{{value0}} cookies could not be decrypted and were skipped for a reason this version of Orca does not recognize. Update Orca to see the details, then try the import again.',
{ value0: warning.failedCookies }
)
}
switch (warning.reason) {
case 'app-bound-encryption':
return warning.otherFailedCookies
@@ -54,6 +93,7 @@ function formatCookieImportWarning(warning: CookieImportWarning): string {
{ value0: warning.failedCookies }
)
}
}
}
}