Skip to content

Commit ba4a283

Browse files
jakolehmNokel81
andauthored
Refactor electron window setCertificateVerifyProc (lensapp#7185)
* refactor electron window setCertificateVerifyProc Signed-off-by: Jari Kolehmainen <[email protected]> * use ChromiumNetError enum in tests Signed-off-by: Jari Kolehmainen <[email protected]> * Fix unit tests Signed-off-by: Sebastian Malton <[email protected]> --------- Signed-off-by: Jari Kolehmainen <[email protected]> Signed-off-by: Sebastian Malton <[email protected]> Co-authored-by: Sebastian Malton <[email protected]>
1 parent 4f2ba5d commit ba4a283

File tree

3 files changed

+117
-19
lines changed

3 files changed

+117
-19
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Copyright (c) OpenLens Authors. All rights reserved.
3+
* Licensed under MIT License. See LICENSE in root directory for more information.
4+
*/
5+
6+
import type { DiContainer } from "@ogre-tools/injectable";
7+
import setupLensProxyCertificateInjectable from "../../../../start-main-application/runnables/setup-lens-proxy-certificate.injectable";
8+
import lensProxyCertificateInjectable from "../../../../../common/certificate/lens-proxy-certificate.injectable";
9+
import { getDiForUnitTesting } from "../../../../getDiForUnitTesting";
10+
import sessionCertificateVerifierInjectable, { ChromiumNetError } from "../session-certificate-verifier.injectable";
11+
12+
const externalCertificate = `-----BEGIN CERTIFICATE-----
13+
MIIFzzCCBLegAwIBAgIQByL1wEn7yGRLqHZvmBzvpTANBgkqhkiG9w0BAQsFADA8
14+
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
15+
UlNBIDIwNDggTTAyMB4XDTIzMDIwOTAwMDAwMFoXDTIzMTAxNDIzNTk1OVowFjEU
16+
MBIGA1UEAxMLazhzbGVucy5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
17+
AoIBAQDNdPm5tKztUpgHgDHjktelNvRsaj4QHTzShUP5p2uGu+lNHhPByp3+fp8p
18+
v6V4PhRyH006RcyvUkQlEOiprP0fF/L16Jlrlo13N7hspVS4drlxE0v4JcLxBKm8
19+
pwsv7bfeZ7g6SWKA/0wbSTk8AyL0rCgcpMUWyPloq3gInO1x7kazgCAgrB34CSdj
20+
JyD1Y8Od8eH8C9qdRlTcV0rG8y2np8YbK1lF77CXjD2feGjiUAMUAtArGKCZOc33
21+
erdhvXgJQ1/SgWcEbbhEZ7j8cfH6y7hPPmU43epyePvY0SZ7x1PBt870W1LjG6lq
22+
pfzqxVVxmT6Txiktnd/6cHCzfxjbAgMBAAGjggLxMIIC7TAfBgNVHSMEGDAWgBTA
23+
MVLNWlDDgnx0cc7L6Zz5euuC4jAdBgNVHQ4EFgQUcC3Qdy61LUiE9hOvDJGYC/yt
24+
fu0wJQYDVR0RBB4wHIILazhzbGVucy5kZXaCDSouazhzbGVucy5kZXYwDgYDVR0P
25+
AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8E
26+
NDAyMDCgLqAshipodHRwOi8vY3JsLnIybTAyLmFtYXpvbnRydXN0LmNvbS9yMm0w
27+
Mi5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsG
28+
AQUFBzABhiFodHRwOi8vb2NzcC5yMm0wMi5hbWF6b250cnVzdC5jb20wNgYIKwYB
29+
BQUHMAKGKmh0dHA6Ly9jcnQucjJtMDIuYW1hem9udHJ1c3QuY29tL3IybTAyLmNl
30+
cjAMBgNVHRMBAf8EAjAAMIIBfAYKKwYBBAHWeQIEAgSCAWwEggFoAWYAdQDoPtDa
31+
PvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYY4etOIAAAEAwBGMEQCIGT/
32+
/BWgTcOFQdzEX2qKlArMTvMwXggEY+m4ervIFLFnAiAyuX0I9jbGBI1XBiQ2mjXT
33+
FIGw3TMF5b4rrCwhkRBG/gB1ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55
34+
NqWaAAABhjh6084AAAQDAEYwRAIgewezL8S3+qwozF4fNt+0FiV95luazD1yKb35
35+
ZeOqudACIC7eFoZsaySOOivbqIp+nr9PB3qD08C1VKoi/LmnDp+3AHYAtz77JN+c
36+
Tbp18jnFulj0bF38Qs96nzXEnh0JgSXttJkAAAGGOHrTlgAABAMARzBFAiAmZyNU
37+
1H54FbGdwwXVXPxNYVE3MUlHswkR56WvWkvJ0wIhAJELvOBDIsCJ5uxTam2Xaxe0
38+
nZ+YTVzXDoQAfHplV1N6MA0GCSqGSIb3DQEBCwUAA4IBAQAghl2vkfW4Gph6Ez/v
39+
EA/INeDXSErm/o3zBv4uTS7kuINPAtTlDtVJW/usw++F5fmgjmyNVc94y35hFG9Q
40+
8LTDgJWvxekmiJJ+FCAxbpkhqXjHhugXwoUvAKktpyFnw+1cliYeA01EevOhnN+n
41+
ux6vjEyhhEZm/JV/TXWaNSmVprXRXwc1m5dQzEEqkXgIhhhSK7E/63L+Zm548cjp
42+
LAp+pJnaHfg0a83QnPWyZeyob+GklQjEdx64i+7wAhhpUp1Ge2TnFfs6zQGv2Y7/
43+
mgyzhHkKlUwQb5pi0rgR4oqKhnItyXjWqN3Y3wefTJblIs2sxEtYEzBUwlQZ3YM/
44+
ycM4
45+
-----END CERTIFICATE-----`;
46+
47+
describe("sessionCertificateVerifier", () => {
48+
let di: DiContainer;
49+
50+
beforeEach(() => {
51+
di = getDiForUnitTesting();
52+
di.unoverride(lensProxyCertificateInjectable);
53+
di.inject(setupLensProxyCertificateInjectable).run();
54+
});
55+
56+
it("marks lens proxy certificate as trusted", () => {
57+
const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable);
58+
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get();
59+
const callback = jest.fn();
60+
61+
sessionCertificateVerifier({
62+
certificate: { data: lensProxyCertificate.cert },
63+
} as any, callback);
64+
65+
expect(callback).toHaveBeenCalledWith(ChromiumNetError.SUCCESS);
66+
});
67+
68+
it("passes verification to chromium on non lens proxy certificate", () => {
69+
const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable);
70+
const callback = jest.fn();
71+
72+
sessionCertificateVerifier({
73+
certificate: { data: externalCertificate },
74+
} as any, callback);
75+
76+
expect(callback).toHaveBeenCalledWith(ChromiumNetError.RESULT_FROM_CHROMIUM);
77+
});
78+
});

packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* Licensed under MIT License. See LICENSE in root directory for more information.
44
*/
55
import { getInjectable } from "@ogre-tools/injectable";
6-
import { timingSafeEqual, X509Certificate } from "crypto";
76
import loggerInjectable from "../../../../common/logger.injectable";
87
import applicationWindowStateInjectable from "./application-window-state.injectable";
98
import { BrowserWindow } from "electron";
@@ -14,8 +13,8 @@ import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path
1413
import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable";
1514
import isLinuxInjectable from "../../../../common/vars/is-linux.injectable";
1615
import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable";
17-
import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable";
1816
import { applicationInformationToken } from "@k8slens/application";
17+
import sessionCertificateVerifierInjectable from "./session-certificate-verifier.injectable";
1918

2019
export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover";
2120

@@ -27,13 +26,6 @@ export interface UrlSource {
2726
}
2827
export type ContentSource = RequireExactlyOne<FileSource & UrlSource>;
2928

30-
// see https://www.electronjs.org/docs/latest/api/session#sessetcertificateverifyprocproc
31-
enum ChromiumNetError {
32-
SUCCESS = 0,
33-
FAILURE = -2,
34-
RESULT_FROM_CHROMIUM = -3,
35-
}
36-
3729
export interface ElectronWindowConfiguration {
3830
id: string;
3931
title: string;
@@ -64,8 +56,7 @@ const createElectronWindowInjectable = getInjectable({
6456
const isLinux = di.inject(isLinuxInjectable);
6557
const applicationInformation = di.inject(applicationInformationToken);
6658
const pathExistsSync = di.inject(pathExistsSyncInjectable);
67-
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get();
68-
const lensProxyX509Cert = new X509Certificate(lensProxyCertificate.cert);
59+
const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable);
6960

7061
return (configuration) => {
7162
const applicationWindowState = di.inject(
@@ -119,14 +110,7 @@ const createElectronWindowInjectable = getInjectable({
119110

120111
applicationWindowState.manage(browserWindow);
121112

122-
browserWindow.webContents.session.setCertificateVerifyProc((request, shouldBeTrusted) => {
123-
const { certificate } = request;
124-
const cert = new X509Certificate(certificate.data);
125-
const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length
126-
&& timingSafeEqual(cert.raw, lensProxyX509Cert.raw);
127-
128-
shouldBeTrusted(shouldTrustCert ? ChromiumNetError.SUCCESS : ChromiumNetError.RESULT_FROM_CHROMIUM);
129-
});
113+
browserWindow.webContents.session.setCertificateVerifyProc(sessionCertificateVerifier);
130114

131115
browserWindow
132116
.on("focus", () => {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Copyright (c) OpenLens Authors. All rights reserved.
3+
* Licensed under MIT License. See LICENSE in root directory for more information.
4+
*/
5+
import { getInjectable } from "@ogre-tools/injectable";
6+
import { timingSafeEqual, X509Certificate } from "crypto";
7+
import type { Request } from "electron";
8+
import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable";
9+
10+
// see https://www.electronjs.org/docs/latest/api/session#sessetcertificateverifyprocproc
11+
export enum ChromiumNetError {
12+
SUCCESS = 0,
13+
FAILURE = -2,
14+
RESULT_FROM_CHROMIUM = -3,
15+
}
16+
17+
export type CertificateVerificationCallback = (error: ChromiumNetError) => void;
18+
19+
const sessionCertificateVerifierInjectable = getInjectable({
20+
id: "session-certificate-verifier",
21+
instantiate: (di) => {
22+
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get();
23+
const lensProxyX509Cert = new X509Certificate(lensProxyCertificate.cert);
24+
25+
return (request: Request, shouldBeTrusted: CertificateVerificationCallback) => {
26+
const { certificate } = request;
27+
const cert = new X509Certificate(certificate.data);
28+
const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length
29+
&& timingSafeEqual(cert.raw, lensProxyX509Cert.raw);
30+
31+
shouldBeTrusted(shouldTrustCert ? ChromiumNetError.SUCCESS : ChromiumNetError.RESULT_FROM_CHROMIUM);
32+
};
33+
},
34+
});
35+
36+
export default sessionCertificateVerifierInjectable;

0 commit comments

Comments
 (0)