From 6ffdf05dc65a0fcd3f60cfe586a04a31e19d6de3 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Thu, 23 Jul 2026 01:09:44 +0800 Subject: [PATCH] fix: enable official desktop browser integration settings Return Bitwarden's desktop-ui-settings-dialog feature state so supported desktop clients use the official settings dialog with browser integration controls. Fixes #315 --- package.json | 1 + scripts/config-compatibility.test.ts | 12 ++++++ src/config-response.ts | 52 ++++++++++++++++++++++++++ src/router-public.ts | 55 +--------------------------- 4 files changed, 66 insertions(+), 54 deletions(-) create mode 100644 scripts/config-compatibility.test.ts create mode 100644 src/config-response.ts diff --git a/package.json b/package.json index 787e3a5..0aff803 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "domains:sync": "node scripts/sync-global-domains.mjs", "i18n": "node scripts/i18n-validate.cjs", "i18n:validate": "node scripts/i18n-validate.cjs", + "test:config-compatibility": "tsx --test scripts/config-compatibility.test.ts", "test:web-crypto": "tsx --test scripts/web-crypto-availability.test.ts", "test:webauthn-mobile": "node --test scripts/webauthn-mobile-connector.test.mjs", "test:webauthn-connector": "node --test scripts/webauthn-connector.test.mjs && tsx --test scripts/webauthn-connector-headers.test.ts", diff --git a/scripts/config-compatibility.test.ts b/scripts/config-compatibility.test.ts new file mode 100644 index 0000000..571192d --- /dev/null +++ b/scripts/config-compatibility.test.ts @@ -0,0 +1,12 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { buildConfigResponse } from '../src/config-response'; + +test('config enables the official Bitwarden desktop settings dialog', () => { + const body = buildConfigResponse('https://vault.example.test'); + + assert.equal(body.featureStates['desktop-ui-settings-dialog'], true); + assert.equal(body.environment.vault, 'https://vault.example.test'); + assert.equal(body.object, 'config'); +}); diff --git a/src/config-response.ts b/src/config-response.ts new file mode 100644 index 0000000..255aae5 --- /dev/null +++ b/src/config-response.ts @@ -0,0 +1,52 @@ +import { LIMITS } from './config/limits'; + +function buildIconServiceTemplate(origin: string): string { + return `${origin}/icons/{}/icon.png`; +} + +function buildIconServiceCsp(origin: string): string { + return `img-src 'self' data: ${origin}`; +} + +export function buildConfigResponse(origin: string) { + const fillAssistBase = `${origin}/fill-assist/`; + return { + version: LIMITS.compatibility.bitwardenServerVersion, + gitHash: 'nodewarden', + server: null, + environment: { + cloudRegion: 'self-hosted', + vault: origin, + api: origin + '/api', + identity: origin + '/identity', + notifications: origin + '/notifications', + icons: origin, + sso: '', + fillAssistRules: fillAssistBase, + }, + push: { + pushTechnology: 0, + vapidPublicKey: null, + }, + communication: null, + settings: { + disableUserRegistration: false, + }, + _icon_service_url: buildIconServiceTemplate(origin), + _icon_service_csp: buildIconServiceCsp(origin), + featureStates: { + 'cipher-key-encryption': LIMITS.compatibility.cipherKeyEncryptionFeatureEnabled, + 'desktop-ui-settings-dialog': true, + 'duo-redirect': true, + 'email-verification': true, + 'fill-assist-targeting-rules': true, + 'pm-19051-send-email-verification': false, + 'pm-19148-innovation-archive': true, + 'pm-4516-devices-add-last-activity-date': true, + 'pm-30529-webauthn-related-origins': true, + 'unauth-ui-refresh': true, + 'web-push': false, + }, + object: 'config', + }; +} diff --git a/src/router-public.ts b/src/router-public.ts index 7b28aef..30f8f3f 100644 --- a/src/router-public.ts +++ b/src/router-public.ts @@ -36,6 +36,7 @@ import { jsonResponse, unsupportedResponse } from './utils/response'; import { StorageService } from './services/storage'; import type { Env } from './types'; import { getConfiguredWebAuthnAllowedOrigins } from './utils/origins'; +import { buildConfigResponse } from './config-response'; type PublicRateLimiter = (category?: string, maxRequests?: number) => Promise; type JwtUnsafeReason = 'missing' | 'too_short' | null; @@ -95,60 +96,6 @@ function handleMissingWebsiteIcon(): Response { }); } -function buildIconServiceBase(origin: string): string { - return `${origin}/icons`; -} - -function buildIconServiceTemplate(origin: string): string { - return `${buildIconServiceBase(origin)}/{}/icon.png`; -} - -function buildIconServiceCsp(origin: string): string { - return `img-src 'self' data: ${origin}`; -} - -function buildConfigResponse(origin: string) { - const fillAssistBase = `${origin}/fill-assist/`; - return { - version: LIMITS.compatibility.bitwardenServerVersion, - gitHash: 'nodewarden', - server: null, - environment: { - cloudRegion: 'self-hosted', - vault: origin, - api: origin + '/api', - identity: origin + '/identity', - notifications: origin + '/notifications', - icons: origin, - sso: '', - fillAssistRules: fillAssistBase, - }, - push: { - pushTechnology: 0, - vapidPublicKey: null, - }, - communication: null, - settings: { - disableUserRegistration: false, - }, - _icon_service_url: buildIconServiceTemplate(origin), - _icon_service_csp: buildIconServiceCsp(origin), - featureStates: { - 'cipher-key-encryption': LIMITS.compatibility.cipherKeyEncryptionFeatureEnabled, - 'duo-redirect': true, - 'email-verification': true, - 'fill-assist-targeting-rules': true, - 'pm-19051-send-email-verification': false, - 'pm-19148-innovation-archive': true, - 'pm-4516-devices-add-last-activity-date': true, - 'pm-30529-webauthn-related-origins': true, - 'unauth-ui-refresh': true, - 'web-push': false, - }, - object: 'config', - }; -} - function normalizeIconHost(rawHost: string): string | null { let decoded: string; try {