From ed073d6562c01217761ebfdf2e540f8821b625b2 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 4 Oct 2022 17:52:55 -0400 Subject: [PATCH] Revert "Remove mac-ca usage since it was only in tests (#6043)" (#6319) This reverts commit f54438661906923606404bfda921a5f93ed1197f. Signed-off-by: Sebastian Malton Signed-off-by: Sebastian Malton --- .../__tests__/app-preferences.tests.ts | 7 +- integration/__tests__/cluster-pages.tests.ts | 3 +- integration/helpers/utils.ts | 8 ++ package.json | 1 + src/common/__tests__/system-ca.test.ts | 99 +++++++++++++++++ .../inject-system-cas.injectable.ts | 39 ------- .../request-system-cas-token.ts | 10 -- .../request-system-cas.injectable.darwin.ts | 44 -------- .../request-system-cas.injectable.linux.ts | 14 --- ...quest-system-cas.injectable.testing-env.ts | 14 --- .../request-system-cas.injectable.win32.ts | 45 -------- src/common/fs/exec-file.injectable.ts | 24 +---- src/common/system-ca.ts | 102 ++++++++++++++++++ src/common/utils/__tests__/paths.test.ts | 7 +- src/jest.setup.ts | 4 +- src/main/getDi.ts | 8 +- .../runnables/setup-system-ca.injectable.ts | 12 ++- .../root-frame/setup-system-ca.injectable.ts | 12 ++- src/renderer/getDi.tsx | 8 +- src/test-utils/skippers.ts | 18 ---- types/from-webpack.d.ts | 8 -- types/mocks.d.ts | 1 + webpack/main.ts | 6 -- webpack/renderer.ts | 6 -- yarn.lock | 12 +++ 25 files changed, 262 insertions(+), 250 deletions(-) create mode 100644 src/common/__tests__/system-ca.test.ts delete mode 100644 src/common/certificate-authorities/inject-system-cas.injectable.ts delete mode 100644 src/common/certificate-authorities/request-system-cas-token.ts delete mode 100644 src/common/certificate-authorities/request-system-cas.injectable.darwin.ts delete mode 100644 src/common/certificate-authorities/request-system-cas.injectable.linux.ts delete mode 100644 src/common/certificate-authorities/request-system-cas.injectable.testing-env.ts delete mode 100644 src/common/certificate-authorities/request-system-cas.injectable.win32.ts create mode 100644 src/common/system-ca.ts delete mode 100644 src/test-utils/skippers.ts delete mode 100644 types/from-webpack.d.ts diff --git a/integration/__tests__/app-preferences.tests.ts b/integration/__tests__/app-preferences.tests.ts index 04a4929c6a..6c0c8fd65b 100644 --- a/integration/__tests__/app-preferences.tests.ts +++ b/integration/__tests__/app-preferences.tests.ts @@ -24,10 +24,9 @@ describe("preferences page tests", () => { await app.evaluate(async ({ app }) => { await app.applicationMenu - ?.getMenuItemById(process.platform === "darwin" ? "root" : "file") - ?.submenu - ?.getMenuItemById("preferences") - ?.click(); + .getMenuItemById(process.platform === "darwin" ? "root" : "file") + .submenu.getMenuItemById("preferences") + .click(); }); }, 10*60*1000); diff --git a/integration/__tests__/cluster-pages.tests.ts b/integration/__tests__/cluster-pages.tests.ts index 4b9cc26b8c..e1474a4332 100644 --- a/integration/__tests__/cluster-pages.tests.ts +++ b/integration/__tests__/cluster-pages.tests.ts @@ -14,11 +14,10 @@ import { minikubeReady } from "../helpers/minikube"; import type { Frame, Page } from "playwright"; import { groupBy, toPairs } from "lodash/fp"; import { pipeline } from "@ogre-tools/fp"; -import { describeIf } from "../../src/test-utils/skippers"; const TEST_NAMESPACE = "integration-tests"; -describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => { +utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => { let window: Page; let cleanup: undefined | (() => Promise); let frame: Frame; diff --git a/integration/helpers/utils.ts b/integration/helpers/utils.ts index 4a39fb2f62..167b3b353f 100644 --- a/integration/helpers/utils.ts +++ b/integration/helpers/utils.ts @@ -18,6 +18,14 @@ export const appPaths: Partial> = { "darwin": "./dist/mac/OpenLens.app/Contents/MacOS/OpenLens", }; +export function itIf(condition: boolean) { + return condition ? it : it.skip; +} + +export function describeIf(condition: boolean) { + return condition ? describe : describe.skip; +} + async function getMainWindow(app: ElectronApplication, timeout = 50_000): Promise { return new Promise((resolve, reject) => { const cleanup = disposer(); diff --git a/package.json b/package.json index e0c0d61863..49fa584d7c 100644 --- a/package.json +++ b/package.json @@ -250,6 +250,7 @@ "js-yaml": "^4.1.0", "jsdom": "^16.7.0", "lodash": "^4.17.15", + "mac-ca": "^1.0.6", "marked": "^4.1.1", "md5-file": "^5.0.0", "mobx": "^6.6.2", diff --git a/src/common/__tests__/system-ca.test.ts b/src/common/__tests__/system-ca.test.ts new file mode 100644 index 0000000000..473fe6ed57 --- /dev/null +++ b/src/common/__tests__/system-ca.test.ts @@ -0,0 +1,99 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import https from "https"; +import os from "os"; +import { getMacRootCA, getWinRootCA, injectCAs, DSTRootCAX3 } from "../system-ca"; +import { dependencies, devDependencies } from "../../../package.json"; +import assert from "assert"; + +const deps = { ...dependencies, ...devDependencies }; + +// Skip the test if mac-ca is not installed, or os is not darwin +(deps["mac-ca"] && os.platform().includes("darwin") ? describe: describe.skip)("inject CA for Mac", () => { + // for reset https.globalAgent.options.ca after testing + let _ca: string | Buffer | (string | Buffer)[] | undefined; + + beforeEach(() => { + _ca = https.globalAgent.options.ca; + }); + + afterEach(() => { + https.globalAgent.options.ca = _ca; + }); + + /** + * The test to ensure using getMacRootCA + injectCAs injects CAs in the same way as using + * the auto injection (require('mac-ca')) + */ + it("should inject the same ca as mac-ca", async () => { + const osxCAs = await getMacRootCA(); + + injectCAs(osxCAs); + const injected = https.globalAgent.options.ca as (string | Buffer)[]; + + await import("mac-ca"); + const injectedByMacCA = https.globalAgent.options.ca as (string | Buffer)[]; + + expect(new Set(injected)).toEqual(new Set(injectedByMacCA)); + }); + + it("shouldn't included the expired DST Root CA X3 on Mac", async () => { + const osxCAs = await getMacRootCA(); + + injectCAs(osxCAs); + const injected = https.globalAgent.options.ca; + + assert(injected); + expect(injected.includes(DSTRootCAX3)).toBeFalsy(); + }); +}); + +// Skip the test if win-ca is not installed, or os is not win32 +(deps["win-ca"] && os.platform().includes("win32") ? describe: describe.skip)("inject CA for Windows", () => { + // for reset https.globalAgent.options.ca after testing + let _ca: string | Buffer | (string | Buffer)[] | undefined; + + beforeEach(() => { + _ca = https.globalAgent.options.ca; + }); + + afterEach(() => { + https.globalAgent.options.ca = _ca; + }); + + /** + * The test to ensure using win-ca/api injects CAs in the same way as using + * the auto injection (require('win-ca').inject('+')) + */ + it("should inject the same ca as winca.inject('+')", async () => { + const winCAs = await getWinRootCA(); + + const wincaAPI = await import("win-ca/api"); + + wincaAPI.inject("+", winCAs); + const injected = https.globalAgent.options.ca as (string | Buffer)[]; + + const winca = await import("win-ca"); + + winca.inject("+"); // see: https://github.com/ukoloff/win-ca#caveats + const injectedByWinCA = https.globalAgent.options.ca as (string | Buffer)[]; + + expect(new Set(injected)).toEqual(new Set(injectedByWinCA)); + }); + + it("shouldn't included the expired DST Root CA X3 on Windows", async () => { + const winCAs = await getWinRootCA(); + + const wincaAPI = await import("win-ca/api"); + + wincaAPI.inject("true", winCAs); + const injected = https.globalAgent.options.ca as (string | Buffer)[]; + + expect(injected.includes(DSTRootCAX3)).toBeFalsy(); + }); +}); + + + diff --git a/src/common/certificate-authorities/inject-system-cas.injectable.ts b/src/common/certificate-authorities/inject-system-cas.injectable.ts deleted file mode 100644 index b6223fa38e..0000000000 --- a/src/common/certificate-authorities/inject-system-cas.injectable.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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 { globalAgent } from "https"; -import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -// DST Root CA X3, which was expired on 9.30.2021 -const DSTRootCAX3 = "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\nPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\nEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\nrz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\nOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\nxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\naeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\nHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\nSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\nikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\nAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\nR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\nJDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\nOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----\n"; - -function isCertActive(cert: string) { - const isExpired = typeof cert !== "string" || cert.includes(DSTRootCAX3); - - return !isExpired; -} - -const injectSystemCAsInjectable = getInjectable({ - id: "inject-system-cas", - instantiate: (di) => { - const requestSystemCAs = di.inject(requestSystemCAsInjectionToken); - - return async () => { - for (const cert of await requestSystemCAs()) { - if (isCertActive(cert)) { - if (Array.isArray(globalAgent.options.ca) && !globalAgent.options.ca.includes(cert)) { - globalAgent.options.ca.push(cert); - } else { - globalAgent.options.ca = [cert]; - } - } - } - }; - }, -}); - -export default injectSystemCAsInjectable; - diff --git a/src/common/certificate-authorities/request-system-cas-token.ts b/src/common/certificate-authorities/request-system-cas-token.ts deleted file mode 100644 index c69b0bd8b0..0000000000 --- a/src/common/certificate-authorities/request-system-cas-token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; - -export const requestSystemCAsInjectionToken = getInjectionToken<() => Promise>({ - id: "request-system-cas-token", -}); diff --git a/src/common/certificate-authorities/request-system-cas.injectable.darwin.ts b/src/common/certificate-authorities/request-system-cas.injectable.darwin.ts deleted file mode 100644 index 7b0425bbe1..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.darwin.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 execFileInjectable from "../fs/exec-file.injectable"; -import loggerInjectable from "../logger.injectable"; -import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions -const certSplitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: (di) => { - const execFile = di.inject(execFileInjectable); - const logger = di.inject(loggerInjectable); - - const execSecurity = async (...args: string[]) => { - const output = await execFile("/usr/bin/security", args); - - return output.split(certSplitPattern); - }; - - return async () => { - try { - const [trusted, rootCA] = await Promise.all([ - execSecurity("find-certificate", "-a", "-p"), - execSecurity("find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain"), - ]); - - return [...new Set([...trusted, ...rootCA])]; - } catch (error) { - logger.warn(`[INJECT-CAS]: Error injecting root CAs from MacOSX: ${error}`); - } - - return []; - }; - }, - causesSideEffects: true, - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/certificate-authorities/request-system-cas.injectable.linux.ts b/src/common/certificate-authorities/request-system-cas.injectable.linux.ts deleted file mode 100644 index 1d7bf10350..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.linux.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: () => async () => [], - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/certificate-authorities/request-system-cas.injectable.testing-env.ts b/src/common/certificate-authorities/request-system-cas.injectable.testing-env.ts deleted file mode 100644 index 1d7bf10350..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.testing-env.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 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 { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: () => async () => [], - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/certificate-authorities/request-system-cas.injectable.win32.ts b/src/common/certificate-authorities/request-system-cas.injectable.win32.ts deleted file mode 100644 index af9366970d..0000000000 --- a/src/common/certificate-authorities/request-system-cas.injectable.win32.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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 execFileInjectable from "../fs/exec-file.injectable"; -import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; - -const pemEncoding = (hexEncodedCert: String) => { - const certData = Buffer.from(hexEncodedCert, "hex").toString("base64"); - const lines = ["-----BEGIN CERTIFICATE-----"]; - - for (let i = 0; i < certData.length; i += 64) { - lines.push(certData.substring(i, i + 64)); - } - - lines.push("-----END CERTIFICATE-----", ""); - - return lines.join("\r\n"); -}; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: (di) => { - const wincaRootsExePath: string = __non_webpack_require__.resolve("win-ca/lib/roots.exe"); - const execFile = di.inject(execFileInjectable); - - return async () => { - /** - * This needs to be done manually because for some reason calling the api from "win-ca" - * directly fails to load "child_process" correctly on renderer - */ - const output = await execFile(wincaRootsExePath); - - return output - .split("\r\n") - .filter(Boolean) - .map(pemEncoding); - }; - }, - causesSideEffects: true, - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/src/common/fs/exec-file.injectable.ts b/src/common/fs/exec-file.injectable.ts index 244f05d0a1..15d0ad48dc 100644 --- a/src/common/fs/exec-file.injectable.ts +++ b/src/common/fs/exec-file.injectable.ts @@ -7,11 +7,7 @@ import type { ExecFileOptions } from "child_process"; import { execFile } from "child_process"; import { promisify } from "util"; -export interface ExecFile { - (filePath: string): Promise; - (filePath: string, argsOrOptions: string[] | ExecFileOptions): Promise; - (filePath: string, args: string[], options: ExecFileOptions): Promise; -} +export type ExecFile = (filePath: string, args: string[], options: ExecFileOptions) => Promise; const execFileInjectable = getInjectable({ id: "exec-file", @@ -19,22 +15,8 @@ const execFileInjectable = getInjectable({ instantiate: (): ExecFile => { const asyncExecFile = promisify(execFile); - return async (filePath: string, argsOrOptions?: string[] | ExecFileOptions, maybeOptions?: ExecFileOptions) => { - let args: string[]; - let options: ExecFileOptions; - - if (Array.isArray(argsOrOptions)) { - args = argsOrOptions; - options = maybeOptions ?? {}; - } else { - args = []; - options = maybeOptions ?? argsOrOptions ?? {}; - } - - const result = await asyncExecFile(filePath, args, { - encoding: "utf-8", - ...options, - }); + return async (filePath, args, options) => { + const result = await asyncExecFile(filePath, args, options); return result.stdout; }; diff --git a/src/common/system-ca.ts b/src/common/system-ca.ts new file mode 100644 index 0000000000..4e57ca2d7b --- /dev/null +++ b/src/common/system-ca.ts @@ -0,0 +1,102 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { isMac, isWindows } from "./vars"; +import wincaAPI from "win-ca/api"; +import https from "https"; +import { promiseExecFile } from "./utils/promise-exec"; + +// DST Root CA X3, which was expired on 9.30.2021 +export const DSTRootCAX3 = "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\nPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\nEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\nrz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\nOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\nxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\naeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\nHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\nSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\nikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\nAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\nR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\nJDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\nOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----\n"; + +export function isCertActive(cert: string) { + const isExpired = typeof cert !== "string" || cert.includes(DSTRootCAX3); + + return !isExpired; +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions +const certSplitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; + +async function execSecurity(...args: string[]): Promise { + const { stdout } = await promiseExecFile("/usr/bin/security", args); + + return stdout.split(certSplitPattern); +} + +/** + * Get root CA certificate from MacOSX system keychain + * Only return non-expred certificates. + */ +export async function getMacRootCA() { + // inspired mac-ca https://github.com/jfromaniello/mac-ca + const [trusted, rootCA] = await Promise.all([ + execSecurity("find-certificate", "-a", "-p"), + execSecurity("find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain"), + ]); + + return [...new Set([...trusted, ...rootCA])].filter(isCertActive); +} + +/** + * Get root CA certificate from Windows system certificate store. + * Only return non-expred certificates. + */ +export function getWinRootCA(): Promise { + return new Promise((resolve) => { + const CAs: string[] = []; + + wincaAPI({ + format: wincaAPI.der2.pem, + inject: false, + ondata: (ca: string) => { + CAs.push(ca); + }, + onend: () => { + resolve(CAs.filter(isCertActive)); + }, + }); + }); +} + + +/** + * Add (or merge) CAs to https.globalAgent.options.ca + */ +export function injectCAs(CAs: string[]) { + for (const cert of CAs) { + if (Array.isArray(https.globalAgent.options.ca) && !https.globalAgent.options.ca.includes(cert)) { + https.globalAgent.options.ca.push(cert); + } else { + https.globalAgent.options.ca = [cert]; + } + } +} + +/** + * Inject CAs found in OS's (Windoes/MacOSX only) root certificate store to https.globalAgent.options.ca + */ +export async function injectSystemCAs() { + if (isMac) { + try { + const osxRootCAs = await getMacRootCA(); + + injectCAs(osxRootCAs); + } catch (error) { + console.warn(`[MAC-CA]: Error injecting root CAs from MacOSX. ${error}`); + } + } + + if (isWindows) { + try { + const winRootCAs = await getWinRootCA(); + + wincaAPI.inject("+", winRootCAs); + + } catch (error) { + console.warn(`[WIN-CA]: Error injecting root CAs from Windows. ${error}`); + } + } +} diff --git a/src/common/utils/__tests__/paths.test.ts b/src/common/utils/__tests__/paths.test.ts index dc9425eb99..f5e8a7ccf5 100644 --- a/src/common/utils/__tests__/paths.test.ts +++ b/src/common/utils/__tests__/paths.test.ts @@ -3,11 +3,12 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { describeIf } from "../../../test-utils/skippers"; +import { describeIf } from "../../../../integration/helpers/utils"; +import { isWindows } from "../../vars"; import { isLogicalChildPath } from "../paths"; describe("isLogicalChildPath", () => { - describeIf(process.platform === "win32")("windows tests", () => { + describeIf(isWindows)("windows tests", () => { it.each([ { parentPath: "C:\\Foo", @@ -39,7 +40,7 @@ describe("isLogicalChildPath", () => { }); }); - describeIf(process.platform !== "win32")("posix tests", () => { + describeIf(!isWindows)("posix tests", () => { it.each([ { parentPath: "/foo", diff --git a/src/jest.setup.ts b/src/jest.setup.ts index c45303e19c..eb1e801f72 100644 --- a/src/jest.setup.ts +++ b/src/jest.setup.ts @@ -58,7 +58,7 @@ const getInjectables = (environment: "renderer" | "main", filePathGlob: string) }), ].map(x => path.resolve(__dirname, x)); -(global as any).rendererInjectablePaths = getInjectables("renderer", "*.{injectable,injectable.testing-env}.{ts,tsx}"); +(global as any).rendererInjectablePaths = getInjectables("renderer", "*.injectable.{ts,tsx}"); (global as any).rendererGlobalOverridePaths = getInjectables("renderer", "*.global-override-for-injectable.{ts,tsx}"); -(global as any).mainInjectablePaths = getInjectables("main", "*.{injectable,injectable.testing-env}.{ts,tsx}"); +(global as any).mainInjectablePaths = getInjectables("main", "*.injectable.{ts,tsx}"); (global as any).mainGlobalOverridePaths = getInjectables("main", "*.global-override-for-injectable.{ts,tsx}"); diff --git a/src/main/getDi.ts b/src/main/getDi.ts index 8b71cca39b..17d9cc36b7 100644 --- a/src/main/getDi.ts +++ b/src/main/getDi.ts @@ -19,10 +19,10 @@ export const getDi = () => { autoRegister({ di, requireContexts: [ - require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), - require.context("../extensions", true, CONTEXT_MATCHER_FOR_NON_FEATURES), - require.context("../common", true, CONTEXT_MATCHER_FOR_NON_FEATURES), - require.context("../features", true, CONTEXT_MATCHER_FOR_FEATURES), + require.context("./", true, /\.injectable\.(ts|tsx)$/), + require.context("../extensions", true, /\.injectable\.(ts|tsx)$/), + require.context("../common", true, /\.injectable\.(ts|tsx)$/), + require.context("../features", true, /.*\/(main|common)\/.*\.injectable\.(ts|tsx)$/), ], }); }); diff --git a/src/main/start-main-application/runnables/setup-system-ca.injectable.ts b/src/main/start-main-application/runnables/setup-system-ca.injectable.ts index b5219dbf4f..94589fca7d 100644 --- a/src/main/start-main-application/runnables/setup-system-ca.injectable.ts +++ b/src/main/start-main-application/runnables/setup-system-ca.injectable.ts @@ -2,16 +2,22 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { injectSystemCAs } from "../../../common/system-ca"; import { getInjectable } from "@ogre-tools/injectable"; import { beforeApplicationIsLoadingInjectionToken } from "../runnable-tokens/before-application-is-loading-injection-token"; -import injectSystemCAsInjectable from "../../../common/certificate-authorities/inject-system-cas.injectable"; const setupSystemCaInjectable = getInjectable({ id: "setup-system-ca", - instantiate: (di) => ({ + + instantiate: () => ({ id: "setup-system-ca", - run: di.inject(injectSystemCAsInjectable), + run: async () => { + await injectSystemCAs(); + }, }), + + causesSideEffects: true, + injectionToken: beforeApplicationIsLoadingInjectionToken, }); diff --git a/src/renderer/frames/root-frame/setup-system-ca.injectable.ts b/src/renderer/frames/root-frame/setup-system-ca.injectable.ts index cc8a0bafcc..3ee10748f9 100644 --- a/src/renderer/frames/root-frame/setup-system-ca.injectable.ts +++ b/src/renderer/frames/root-frame/setup-system-ca.injectable.ts @@ -2,16 +2,22 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { injectSystemCAs } from "../../../common/system-ca"; import { getInjectable } from "@ogre-tools/injectable"; import { beforeFrameStartsInjectionToken } from "../../before-frame-starts/before-frame-starts-injection-token"; -import injectSystemCAsInjectable from "../../../common/certificate-authorities/inject-system-cas.injectable"; const setupSystemCaInjectable = getInjectable({ id: "setup-system-ca", - instantiate: (di) => ({ + + instantiate: () => ({ id: "setup-system-ca", - run: di.inject(injectSystemCAsInjectable), + run: async () => { + await injectSystemCAs(); + }, }), + + causesSideEffects: true, + injectionToken: beforeFrameStartsInjectionToken, }); diff --git a/src/renderer/getDi.tsx b/src/renderer/getDi.tsx index 28e111bc97..348c5e369b 100644 --- a/src/renderer/getDi.tsx +++ b/src/renderer/getDi.tsx @@ -20,10 +20,10 @@ export const getDi = () => { autoRegister({ di, requireContexts: [ - require.context("./", true, CONTEXT_MATCHER_FOR_NON_FEATURES), - require.context("../common", true, CONTEXT_MATCHER_FOR_NON_FEATURES), - require.context("../extensions", true, CONTEXT_MATCHER_FOR_NON_FEATURES), - require.context("../features", true, CONTEXT_MATCHER_FOR_FEATURES), + require.context("./", true, /\.injectable\.(ts|tsx)$/), + require.context("../common", true, /\.injectable\.(ts|tsx)$/), + require.context("../extensions", true, /\.injectable\.(ts|tsx)$/), + require.context("../features", true, /.*\/(renderer|common)\/.*\.injectable\.(ts|tsx)$/), ], }); }); diff --git a/src/test-utils/skippers.ts b/src/test-utils/skippers.ts deleted file mode 100644 index bd8e094308..0000000000 --- a/src/test-utils/skippers.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * Conditionally run a test - */ -export function itIf(condition: boolean) { - return condition ? it : it.skip; -} - -/** - * Conditionally run a block of tests - */ -export function describeIf(condition: boolean) { - return condition ? describe : describe.skip; -} diff --git a/types/from-webpack.d.ts b/types/from-webpack.d.ts deleted file mode 100644 index c0b5439355..0000000000 --- a/types/from-webpack.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// These variables will be replaced by webpack at compile time -declare const CONTEXT_MATCHER_FOR_NON_FEATURES: RegExp; -declare const CONTEXT_MATCHER_FOR_FEATURES: RegExp; diff --git a/types/mocks.d.ts b/types/mocks.d.ts index 3826aac829..fd01e929d6 100644 --- a/types/mocks.d.ts +++ b/types/mocks.d.ts @@ -2,6 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ +declare module "mac-ca" declare module "win-ca" declare module "win-ca/api" diff --git a/webpack/main.ts b/webpack/main.ts index 84efd63f22..397ffb2968 100755 --- a/webpack/main.ts +++ b/webpack/main.ts @@ -11,9 +11,7 @@ import getTypeScriptLoader from "./get-typescript-loader"; import CircularDependencyPlugin from "circular-dependency-plugin"; import { iconsAndImagesWebpackRules } from "./renderer"; import type { WebpackPluginInstance } from "webpack"; -import { DefinePlugin } from "webpack"; import { buildDir, isDevelopment, mainDir } from "./vars"; -import { platform } from "process"; const configs: { (): webpack.Configuration }[] = []; @@ -55,10 +53,6 @@ configs.push((): webpack.Configuration => { ], }, plugins: [ - new DefinePlugin({ - CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, - CONTEXT_MATCHER_FOR_FEATURES: `/\\/(main|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, - }), new ForkTsCheckerPlugin(), new CircularDependencyPlugin({ cwd: __dirname, diff --git a/webpack/renderer.ts b/webpack/renderer.ts index 6bad203ca7..c8c87e856f 100755 --- a/webpack/renderer.ts +++ b/webpack/renderer.ts @@ -12,10 +12,8 @@ import MonacoWebpackPlugin from "monaco-editor-webpack-plugin"; import CircularDependencyPlugin from "circular-dependency-plugin"; import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin"; import type { WebpackPluginInstance } from "webpack"; -import { DefinePlugin } from "webpack"; import getTypescriptLoader from "./get-typescript-loader"; import { assetsFolderName, isDevelopment, rendererDir, buildDir, appName, htmlTemplate, publicPath, sassCommonVars } from "./vars"; -import { platform } from "process"; export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration { if (showVars) { @@ -87,10 +85,6 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura }, plugins: [ - new DefinePlugin({ - CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, - CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, - }), new ForkTsCheckerPlugin(), // see also: https://github.com/Microsoft/monaco-editor-webpack-plugin#options diff --git a/yarn.lock b/yarn.lock index e496ec0e2f..4b8d99255c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8765,6 +8765,13 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= +mac-ca@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/mac-ca/-/mac-ca-1.0.6.tgz#89860edfeebcc4593567044281ab3500961ec15f" + integrity sha512-uuCaT+41YtIQlDDvbigP1evK1iUk97zRirP9+8rZJz8x0eIQZG8Z7YQegMTsCiMesLPb6LBgCS95uyAvVA1tmg== + dependencies: + node-forge "^0.10.0" + make-dir@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -9341,6 +9348,11 @@ node-fetch@2.6.7, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + node-forge@^1, node-forge@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"