Files
orca/src/main/startup/select-linux-keyring-backend.ts
T
Neil 23a874a2a1 fix(secrets): seal credentials on Linux desktops Chromium cannot detect (#24035)
* fix(secrets): stop telling Linux users to install a keyring they already run

On a desktop Chromium does not recognise (Hyprland, sway, river, niri) the
selected backend is basic_text and sealing is unavailable, so the at-rest
protection report told the user to install and unlock gnome-keyring — which is
usually already running and serving org.freedesktop.secrets. Chromium simply
never looked, because it picks the backend from XDG_CURRENT_DESKTOP.

Split the Linux unavailable path on the selected backend: an unrecognised
desktop now says so, names XDG_CURRENT_DESKTOP, and points at
--password-store. A backend that did resolve but cannot seal keeps the
install-and-unlock text, which is correct there.

The backend read stays after isEncryptionAvailable(), the call that performs
the D-Bus probe, so nothing new blocks (STA-5765 timing unchanged).

* fix(secrets): seal credentials on Linux desktops Chromium cannot detect

Chromium picks its os_crypt backend from the desktop-environment env vars and
recognises none of the tiling compositors (Hyprland, sway, river, niri). On
those it selects basic_text, whose key Electron only exposes after an explicit
setUsePlainTextEncryption() this app never calls — so isEncryptionAvailable()
is false and every credential store takes its plaintext fallback, while
gnome-keyring sits on the session bus unasked (#21827).

Name gnome-libsecret ourselves, but only where it provably cannot hurt:

- Never for a desktop Chromium does resolve. Overriding a working selection is
  the one change that could strand already-sealed credentials, and a KDE
  session identified only by KDE_FULL_SESSION is the case that matters.
- Only when the secret service's default collection is present AND unlocked,
  measured out of process with a killable 1.5s deadline. A locked collection
  with no unlock prompter is what made isEncryptionAvailable() block for 76s to
  first window (STA-5765); selecting libsecret there would trade silent
  plaintext for a frozen app. The probe reads the Locked property, which cannot
  itself trigger a prompt.

Anything unexpected — no session bus, no gdbus, no name owner, timeout — leaves
Chromium's own choice alone, so the worst case is today's behaviour.

The desktop-presence env vars come from a review finding by @Raajik on #21831,
confirmed there against base/nix/xdg_util.cc.
2026-09-30 02:36:22 -07:00

32 lines
1.3 KiB
TypeScript

import { app } from 'electron'
import { resolveLinuxPasswordStore } from './linux-keyring-backend-selection'
import { probeSecretServiceCollection } from './linux-secret-service-probe'
/**
* Name Chromium's credential backend when its own detection would give up (#21827).
*
* Must run before Electron builds its os_crypt config, which it does while starting
* browser main parts — so before app ready, and before anything touches safeStorage.
*/
export function selectLinuxKeyringBackend(
log: (message: string) => void = (message) => console.log(message)
): void {
if (process.platform !== 'linux') {
return
}
const decision = resolveLinuxPasswordStore({
env: process.env,
passwordStoreAlreadySelected:
app.commandLine.hasSwitch('password-store') ||
process.argv.some((argument) => argument.startsWith('--password-store')),
probeCollection: probeSecretServiceCollection
})
if (!decision.store) {
return
}
app.commandLine.appendSwitch('password-store', decision.store)
// Why logged only when it fires: the no-op cases are every ordinary Linux launch, and
// this line is the only record that the app changed its own credential backend.
log(`[secrets] selected --password-store=${decision.store}: ${decision.reason}`)
}