1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Refactor electron window setCertificateVerifyProc (#7185)

* refactor electron window setCertificateVerifyProc

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* use ChromiumNetError enum in tests

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* Fix unit tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

---------

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
Signed-off-by: Sebastian Malton <sebastian@malton.name>
Co-authored-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Jari Kolehmainen 2023-03-28 16:42:51 +03:00 committed by GitHub
parent 4f2ba5df48
commit ba4a283af9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 117 additions and 19 deletions

View File

@ -0,0 +1,78 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainer } from "@ogre-tools/injectable";
import setupLensProxyCertificateInjectable from "../../../../start-main-application/runnables/setup-lens-proxy-certificate.injectable";
import lensProxyCertificateInjectable from "../../../../../common/certificate/lens-proxy-certificate.injectable";
import { getDiForUnitTesting } from "../../../../getDiForUnitTesting";
import sessionCertificateVerifierInjectable, { ChromiumNetError } from "../session-certificate-verifier.injectable";
const externalCertificate = `-----BEGIN CERTIFICATE-----
MIIFzzCCBLegAwIBAgIQByL1wEn7yGRLqHZvmBzvpTANBgkqhkiG9w0BAQsFADA8
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
UlNBIDIwNDggTTAyMB4XDTIzMDIwOTAwMDAwMFoXDTIzMTAxNDIzNTk1OVowFjEU
MBIGA1UEAxMLazhzbGVucy5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDNdPm5tKztUpgHgDHjktelNvRsaj4QHTzShUP5p2uGu+lNHhPByp3+fp8p
v6V4PhRyH006RcyvUkQlEOiprP0fF/L16Jlrlo13N7hspVS4drlxE0v4JcLxBKm8
pwsv7bfeZ7g6SWKA/0wbSTk8AyL0rCgcpMUWyPloq3gInO1x7kazgCAgrB34CSdj
JyD1Y8Od8eH8C9qdRlTcV0rG8y2np8YbK1lF77CXjD2feGjiUAMUAtArGKCZOc33
erdhvXgJQ1/SgWcEbbhEZ7j8cfH6y7hPPmU43epyePvY0SZ7x1PBt870W1LjG6lq
pfzqxVVxmT6Txiktnd/6cHCzfxjbAgMBAAGjggLxMIIC7TAfBgNVHSMEGDAWgBTA
MVLNWlDDgnx0cc7L6Zz5euuC4jAdBgNVHQ4EFgQUcC3Qdy61LUiE9hOvDJGYC/yt
fu0wJQYDVR0RBB4wHIILazhzbGVucy5kZXaCDSouazhzbGVucy5kZXYwDgYDVR0P
AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8E
NDAyMDCgLqAshipodHRwOi8vY3JsLnIybTAyLmFtYXpvbnRydXN0LmNvbS9yMm0w
Mi5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsG
AQUFBzABhiFodHRwOi8vb2NzcC5yMm0wMi5hbWF6b250cnVzdC5jb20wNgYIKwYB
BQUHMAKGKmh0dHA6Ly9jcnQucjJtMDIuYW1hem9udHJ1c3QuY29tL3IybTAyLmNl
cjAMBgNVHRMBAf8EAjAAMIIBfAYKKwYBBAHWeQIEAgSCAWwEggFoAWYAdQDoPtDa
PvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYY4etOIAAAEAwBGMEQCIGT/
/BWgTcOFQdzEX2qKlArMTvMwXggEY+m4ervIFLFnAiAyuX0I9jbGBI1XBiQ2mjXT
FIGw3TMF5b4rrCwhkRBG/gB1ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55
NqWaAAABhjh6084AAAQDAEYwRAIgewezL8S3+qwozF4fNt+0FiV95luazD1yKb35
ZeOqudACIC7eFoZsaySOOivbqIp+nr9PB3qD08C1VKoi/LmnDp+3AHYAtz77JN+c
Tbp18jnFulj0bF38Qs96nzXEnh0JgSXttJkAAAGGOHrTlgAABAMARzBFAiAmZyNU
1H54FbGdwwXVXPxNYVE3MUlHswkR56WvWkvJ0wIhAJELvOBDIsCJ5uxTam2Xaxe0
nZ+YTVzXDoQAfHplV1N6MA0GCSqGSIb3DQEBCwUAA4IBAQAghl2vkfW4Gph6Ez/v
EA/INeDXSErm/o3zBv4uTS7kuINPAtTlDtVJW/usw++F5fmgjmyNVc94y35hFG9Q
8LTDgJWvxekmiJJ+FCAxbpkhqXjHhugXwoUvAKktpyFnw+1cliYeA01EevOhnN+n
ux6vjEyhhEZm/JV/TXWaNSmVprXRXwc1m5dQzEEqkXgIhhhSK7E/63L+Zm548cjp
LAp+pJnaHfg0a83QnPWyZeyob+GklQjEdx64i+7wAhhpUp1Ge2TnFfs6zQGv2Y7/
mgyzhHkKlUwQb5pi0rgR4oqKhnItyXjWqN3Y3wefTJblIs2sxEtYEzBUwlQZ3YM/
ycM4
-----END CERTIFICATE-----`;
describe("sessionCertificateVerifier", () => {
let di: DiContainer;
beforeEach(() => {
di = getDiForUnitTesting();
di.unoverride(lensProxyCertificateInjectable);
di.inject(setupLensProxyCertificateInjectable).run();
});
it("marks lens proxy certificate as trusted", () => {
const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable);
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get();
const callback = jest.fn();
sessionCertificateVerifier({
certificate: { data: lensProxyCertificate.cert },
} as any, callback);
expect(callback).toHaveBeenCalledWith(ChromiumNetError.SUCCESS);
});
it("passes verification to chromium on non lens proxy certificate", () => {
const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable);
const callback = jest.fn();
sessionCertificateVerifier({
certificate: { data: externalCertificate },
} as any, callback);
expect(callback).toHaveBeenCalledWith(ChromiumNetError.RESULT_FROM_CHROMIUM);
});
});

View File

@ -3,7 +3,6 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { timingSafeEqual, X509Certificate } from "crypto";
import loggerInjectable from "../../../../common/logger.injectable"; import loggerInjectable from "../../../../common/logger.injectable";
import applicationWindowStateInjectable from "./application-window-state.injectable"; import applicationWindowStateInjectable from "./application-window-state.injectable";
import { BrowserWindow } from "electron"; import { BrowserWindow } from "electron";
@ -14,8 +13,8 @@ import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path
import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable"; import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable";
import isLinuxInjectable from "../../../../common/vars/is-linux.injectable"; import isLinuxInjectable from "../../../../common/vars/is-linux.injectable";
import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable"; import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable";
import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable";
import { applicationInformationToken } from "@k8slens/application"; import { applicationInformationToken } from "@k8slens/application";
import sessionCertificateVerifierInjectable from "./session-certificate-verifier.injectable";
export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover"; export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover";
@ -27,13 +26,6 @@ export interface UrlSource {
} }
export type ContentSource = RequireExactlyOne<FileSource & UrlSource>; export type ContentSource = RequireExactlyOne<FileSource & UrlSource>;
// see https://www.electronjs.org/docs/latest/api/session#sessetcertificateverifyprocproc
enum ChromiumNetError {
SUCCESS = 0,
FAILURE = -2,
RESULT_FROM_CHROMIUM = -3,
}
export interface ElectronWindowConfiguration { export interface ElectronWindowConfiguration {
id: string; id: string;
title: string; title: string;
@ -64,8 +56,7 @@ const createElectronWindowInjectable = getInjectable({
const isLinux = di.inject(isLinuxInjectable); const isLinux = di.inject(isLinuxInjectable);
const applicationInformation = di.inject(applicationInformationToken); const applicationInformation = di.inject(applicationInformationToken);
const pathExistsSync = di.inject(pathExistsSyncInjectable); const pathExistsSync = di.inject(pathExistsSyncInjectable);
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get(); const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable);
const lensProxyX509Cert = new X509Certificate(lensProxyCertificate.cert);
return (configuration) => { return (configuration) => {
const applicationWindowState = di.inject( const applicationWindowState = di.inject(
@ -119,14 +110,7 @@ const createElectronWindowInjectable = getInjectable({
applicationWindowState.manage(browserWindow); applicationWindowState.manage(browserWindow);
browserWindow.webContents.session.setCertificateVerifyProc((request, shouldBeTrusted) => { browserWindow.webContents.session.setCertificateVerifyProc(sessionCertificateVerifier);
const { certificate } = request;
const cert = new X509Certificate(certificate.data);
const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length
&& timingSafeEqual(cert.raw, lensProxyX509Cert.raw);
shouldBeTrusted(shouldTrustCert ? ChromiumNetError.SUCCESS : ChromiumNetError.RESULT_FROM_CHROMIUM);
});
browserWindow browserWindow
.on("focus", () => { .on("focus", () => {

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { timingSafeEqual, X509Certificate } from "crypto";
import type { Request } from "electron";
import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable";
// see https://www.electronjs.org/docs/latest/api/session#sessetcertificateverifyprocproc
export enum ChromiumNetError {
SUCCESS = 0,
FAILURE = -2,
RESULT_FROM_CHROMIUM = -3,
}
export type CertificateVerificationCallback = (error: ChromiumNetError) => void;
const sessionCertificateVerifierInjectable = getInjectable({
id: "session-certificate-verifier",
instantiate: (di) => {
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get();
const lensProxyX509Cert = new X509Certificate(lensProxyCertificate.cert);
return (request: Request, shouldBeTrusted: CertificateVerificationCallback) => {
const { certificate } = request;
const cert = new X509Certificate(certificate.data);
const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length
&& timingSafeEqual(cert.raw, lensProxyX509Cert.raw);
shouldBeTrusted(shouldTrustCert ? ChromiumNetError.SUCCESS : ChromiumNetError.RESULT_FROM_CHROMIUM);
};
},
});
export default sessionCertificateVerifierInjectable;