Cross-platform packages / Validate source (push) Waiting to run
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (arm64, macos, macos-15) (push) Blocked by required conditions
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (arm64, windows, windows-2025) (push) Blocked by required conditions
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (x64, linux, ubuntu-24.04) (push) Blocked by required conditions
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (x64, macos, macos-15-intel) (push) Blocked by required conditions
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (x64, windows, windows-2025) (push) Blocked by required conditions
Cross-platform packages / Publish GitHub Release (push) Blocked by required conditions
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (arm64, linux, ubuntu-24.04-arm) (push) Blocked by required conditions
142 lines
4.0 KiB
TypeScript
142 lines
4.0 KiB
TypeScript
import type { App, Certificate, Event, WebContents } from 'electron'
|
|
import { globalAgent as nodeHttpsGlobalAgent } from 'node:https'
|
|
import {
|
|
Agent,
|
|
getGlobalDispatcher,
|
|
setGlobalDispatcher,
|
|
type Dispatcher
|
|
} from 'undici'
|
|
|
|
type CertificateErrorListener = (
|
|
event: Event,
|
|
webContents: WebContents,
|
|
url: string,
|
|
error: string,
|
|
certificate: Certificate,
|
|
callback: (isTrusted: boolean) => void,
|
|
isMainFrame: boolean
|
|
) => void
|
|
|
|
type CertificateErrorApp = Pick<App, 'on' | 'removeListener'>
|
|
|
|
type GlobalTlsPolicyDependencies = {
|
|
environment: NodeJS.ProcessEnv
|
|
getDispatcher: () => Dispatcher
|
|
setDispatcher: (dispatcher: Dispatcher) => void
|
|
createInsecureDispatcher: () => Dispatcher
|
|
resetNodeHttpsConnections?: () => void
|
|
}
|
|
|
|
const defaultDependencies: GlobalTlsPolicyDependencies = {
|
|
environment: process.env,
|
|
getDispatcher: getGlobalDispatcher,
|
|
setDispatcher: setGlobalDispatcher,
|
|
createInsecureDispatcher: () =>
|
|
new Agent({
|
|
connect: {
|
|
rejectUnauthorized: false
|
|
}
|
|
}),
|
|
resetNodeHttpsConnections: () => nodeHttpsGlobalAgent.destroy()
|
|
}
|
|
|
|
let controlledChildTlsCompatibilityEnabled = false
|
|
|
|
export function isControlledChildTlsCompatibilityEnabled(): boolean {
|
|
return controlledChildTlsCompatibilityEnabled
|
|
}
|
|
|
|
/**
|
|
* Applies invalid-certificate compatibility to network traffic owned by this
|
|
* Electron process. URLs opened with an external OS browser are outside the
|
|
* process and continue to use that browser's certificate policy.
|
|
*/
|
|
export class GlobalTlsPolicy {
|
|
private readonly originalDispatcher: Dispatcher
|
|
private readonly originalNodeTlsValue: string | undefined
|
|
private readonly hadOriginalNodeTlsValue: boolean
|
|
private insecureDispatcher?: Dispatcher
|
|
private enabled = false
|
|
private certificateErrorListenerInstalled = false
|
|
|
|
private readonly certificateErrorListener: CertificateErrorListener = (
|
|
event,
|
|
...parameters
|
|
) => {
|
|
const callback = parameters[4]
|
|
event.preventDefault()
|
|
callback(true)
|
|
}
|
|
|
|
constructor(
|
|
private readonly app: CertificateErrorApp,
|
|
private readonly dependencies: GlobalTlsPolicyDependencies =
|
|
defaultDependencies
|
|
) {
|
|
this.originalDispatcher = dependencies.getDispatcher()
|
|
this.hadOriginalNodeTlsValue = Object.prototype.hasOwnProperty.call(
|
|
dependencies.environment,
|
|
'NODE_TLS_REJECT_UNAUTHORIZED'
|
|
)
|
|
this.originalNodeTlsValue =
|
|
dependencies.environment.NODE_TLS_REJECT_UNAUTHORIZED
|
|
}
|
|
|
|
apply(enabled: boolean): void {
|
|
if (enabled) {
|
|
this.enable()
|
|
return
|
|
}
|
|
this.disable()
|
|
}
|
|
|
|
async dispose(): Promise<void> {
|
|
this.disable()
|
|
await this.insecureDispatcher?.close()
|
|
this.insecureDispatcher = undefined
|
|
}
|
|
|
|
private enable(): void {
|
|
if (this.enabled) {
|
|
return
|
|
}
|
|
this.insecureDispatcher ??=
|
|
this.dependencies.createInsecureDispatcher()
|
|
this.dependencies.environment.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
|
this.dependencies.setDispatcher(this.insecureDispatcher)
|
|
if (!this.certificateErrorListenerInstalled) {
|
|
this.app.on(
|
|
'certificate-error',
|
|
this.certificateErrorListener
|
|
)
|
|
this.certificateErrorListenerInstalled = true
|
|
}
|
|
controlledChildTlsCompatibilityEnabled = true
|
|
this.enabled = true
|
|
}
|
|
|
|
private disable(): void {
|
|
const wasEnabled = this.enabled
|
|
if (this.hadOriginalNodeTlsValue) {
|
|
this.dependencies.environment.NODE_TLS_REJECT_UNAUTHORIZED =
|
|
this.originalNodeTlsValue
|
|
} else {
|
|
delete this.dependencies.environment
|
|
.NODE_TLS_REJECT_UNAUTHORIZED
|
|
}
|
|
this.dependencies.setDispatcher(this.originalDispatcher)
|
|
if (this.certificateErrorListenerInstalled) {
|
|
this.app.removeListener(
|
|
'certificate-error',
|
|
this.certificateErrorListener
|
|
)
|
|
this.certificateErrorListenerInstalled = false
|
|
}
|
|
if (wasEnabled) {
|
|
this.dependencies.resetNodeHttpsConnections?.()
|
|
}
|
|
controlledChildTlsCompatibilityEnabled = false
|
|
this.enabled = false
|
|
}
|
|
}
|