fix(desktop): trust Windows system CAs for remote gateways (#66304)
* fix(desktop): trust Windows system CAs for remote gateways Load Windows-trusted roots into Node's default TLS context before Desktop probes remote backends, while preserving bundled and extra CAs. * test(desktop): cover Windows system CA installation Verify existing trust roots survive the merge and that unsupported or unavailable stores fail open without changing TLS defaults.
This commit is contained in:
parent
1e01a4bbe7
commit
38233b61c2
3 changed files with 164 additions and 0 deletions
|
|
@ -5,6 +5,7 @@ import http from 'node:http'
|
|||
import https from 'node:https'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import tls from 'node:tls'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
import {
|
||||
|
|
@ -142,6 +143,7 @@ import {
|
|||
getVenvSitePackagesEntries,
|
||||
resolveVenvHermesCommand
|
||||
} from './windows-hermes-path'
|
||||
import { installWindowsSystemCaTrust } from './windows-system-ca'
|
||||
import { readWindowsUserEnvVar } from './windows-user-env'
|
||||
import { isPackagedInstallPath as isPackagedInstallPathUnderRoots } from './workspace-cwd'
|
||||
import { readWslWindowsClipboardImage } from './wsl-clipboard-image'
|
||||
|
|
@ -9154,6 +9156,16 @@ app.on('open-url', (event, url) => {
|
|||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const systemCa = installWindowsSystemCaTrust(tls)
|
||||
|
||||
if (systemCa.applied) {
|
||||
rememberLog(
|
||||
`[tls] trusting ${systemCa.systemCertificateCount} Windows system CA certificate(s) for backend connections`
|
||||
)
|
||||
} else if (systemCa.error) {
|
||||
rememberLog(`[tls] could not load Windows system CA certificates: ${systemCa.error}`)
|
||||
}
|
||||
|
||||
if (IS_MAC) {
|
||||
Menu.setApplicationMenu(buildApplicationMenu())
|
||||
} else {
|
||||
|
|
|
|||
96
apps/desktop/electron/windows-system-ca.test.ts
Normal file
96
apps/desktop/electron/windows-system-ca.test.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import assert from 'node:assert/strict'
|
||||
|
||||
import { test } from 'vitest'
|
||||
|
||||
import { installWindowsSystemCaTrust, type NodeTlsCaApi } from './windows-system-ca'
|
||||
|
||||
function fakeTlsApi(
|
||||
defaults: string[] = ['bundled-ca', 'extra-ca'],
|
||||
system: string[] = ['windows-root-ca']
|
||||
): NodeTlsCaApi & { installed: string[][] } {
|
||||
const installed: string[][] = []
|
||||
|
||||
return {
|
||||
installed,
|
||||
getCACertificates(type = 'default') {
|
||||
return type === 'system' ? [...system] : [...defaults]
|
||||
},
|
||||
setDefaultCACertificates(certificates) {
|
||||
installed.push([...certificates])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test('installs Windows system CAs without dropping existing defaults', () => {
|
||||
const tlsApi = fakeTlsApi(['mozilla-root', 'extra-ca'], ['machine-root', 'user-root'])
|
||||
|
||||
const result = installWindowsSystemCaTrust(tlsApi, 'win32')
|
||||
|
||||
assert.deepEqual(tlsApi.installed, [['mozilla-root', 'extra-ca', 'machine-root', 'user-root']])
|
||||
assert.deepEqual(result, {
|
||||
applied: true,
|
||||
systemCertificateCount: 2,
|
||||
totalCertificateCount: 4
|
||||
})
|
||||
})
|
||||
|
||||
test('does not inspect or replace CAs outside Windows', () => {
|
||||
let reads = 0
|
||||
|
||||
const tlsApi: NodeTlsCaApi = {
|
||||
getCACertificates() {
|
||||
reads += 1
|
||||
|
||||
return []
|
||||
},
|
||||
setDefaultCACertificates() {
|
||||
throw new Error('should not install')
|
||||
}
|
||||
}
|
||||
|
||||
const result = installWindowsSystemCaTrust(tlsApi, 'darwin')
|
||||
|
||||
assert.equal(reads, 0)
|
||||
assert.deepEqual(result, {
|
||||
applied: false,
|
||||
systemCertificateCount: 0,
|
||||
totalCertificateCount: 0
|
||||
})
|
||||
})
|
||||
|
||||
test('leaves the existing defaults untouched when Windows has no system CAs', () => {
|
||||
const tlsApi = fakeTlsApi(['mozilla-root'], [])
|
||||
|
||||
const result = installWindowsSystemCaTrust(tlsApi, 'win32')
|
||||
|
||||
assert.deepEqual(tlsApi.installed, [])
|
||||
assert.deepEqual(result, {
|
||||
applied: false,
|
||||
systemCertificateCount: 0,
|
||||
totalCertificateCount: 1
|
||||
})
|
||||
})
|
||||
|
||||
test('fails open when the runtime cannot load the Windows certificate store', () => {
|
||||
const tlsApi: NodeTlsCaApi = {
|
||||
getCACertificates(type = 'default') {
|
||||
if (type === 'system') {
|
||||
throw new Error('certificate store unavailable')
|
||||
}
|
||||
|
||||
return ['mozilla-root']
|
||||
},
|
||||
setDefaultCACertificates() {
|
||||
throw new Error('should not install')
|
||||
}
|
||||
}
|
||||
|
||||
const result = installWindowsSystemCaTrust(tlsApi, 'win32')
|
||||
|
||||
assert.deepEqual(result, {
|
||||
applied: false,
|
||||
systemCertificateCount: 0,
|
||||
totalCertificateCount: 0,
|
||||
error: 'certificate store unavailable'
|
||||
})
|
||||
})
|
||||
56
apps/desktop/electron/windows-system-ca.ts
Normal file
56
apps/desktop/electron/windows-system-ca.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
interface NodeTlsCaApi {
|
||||
getCACertificates(type?: 'default' | 'system'): string[]
|
||||
setDefaultCACertificates(certificates: string[]): void
|
||||
}
|
||||
|
||||
interface WindowsSystemCaResult {
|
||||
applied: boolean
|
||||
systemCertificateCount: number
|
||||
totalCertificateCount: number
|
||||
error?: string
|
||||
}
|
||||
|
||||
function installWindowsSystemCaTrust(
|
||||
tlsApi: NodeTlsCaApi,
|
||||
platform = process.platform
|
||||
): WindowsSystemCaResult {
|
||||
if (platform !== 'win32') {
|
||||
return {
|
||||
applied: false,
|
||||
systemCertificateCount: 0,
|
||||
totalCertificateCount: 0
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const defaultCertificates = tlsApi.getCACertificates('default')
|
||||
const systemCertificates = tlsApi.getCACertificates('system')
|
||||
|
||||
if (systemCertificates.length === 0) {
|
||||
return {
|
||||
applied: false,
|
||||
systemCertificateCount: 0,
|
||||
totalCertificateCount: defaultCertificates.length
|
||||
}
|
||||
}
|
||||
|
||||
const certificates = [...defaultCertificates, ...systemCertificates]
|
||||
tlsApi.setDefaultCACertificates(certificates)
|
||||
|
||||
return {
|
||||
applied: true,
|
||||
systemCertificateCount: systemCertificates.length,
|
||||
totalCertificateCount: certificates.length
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
applied: false,
|
||||
systemCertificateCount: 0,
|
||||
totalCertificateCount: 0,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { installWindowsSystemCaTrust }
|
||||
export type { NodeTlsCaApi, WindowsSystemCaResult }
|
||||
Loading…
Add table
Add a link
Reference in a new issue