From 711181751f0a5ee183b74514a621e4aaa6da3dd7 Mon Sep 17 00:00:00 2001 From: André Oliveira <37463445+SpecialAro@users.noreply.github.com> Date: Fri, 26 Jan 2024 02:06:35 +0000 Subject: feat: self signed certificates bypass (#1545) * feat: self signed certificates bypass * fix lint and vscode setting * Fix some mistakes and comments * forgot this one [skip ci] --- .vscode/settings.json | 2 +- src/@types/stores.types.ts | 1 + .../settings/settings/EditSettingsForm.tsx | 61 +++++++++++++++++++++- src/config.ts | 1 + src/containers/settings/EditSettingsScreen.tsx | 16 ++++++ src/environment-remote.ts | 4 ++ src/helpers/certs-helpers.ts | 25 +++++++++ src/i18n/locales/en-US.json | 6 ++- src/index.ts | 39 ++++++++++++++ 9 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 src/helpers/certs-helpers.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 5060e4994..42a06efe9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,7 +17,7 @@ // "explorer.confirmDelete": false, // "explorer.confirmDragAndDrop": false, - "eslint.runtime": "node", + // "eslint.runtime": "node", "eslint.format.enable": true, "eslint.workingDirectories": [{ "mode": "auto" }], // "eslint.packageManager": "npm", diff --git a/src/@types/stores.types.ts b/src/@types/stores.types.ts index d036f8ee1..c0670ee87 100644 --- a/src/@types/stores.types.ts +++ b/src/@types/stores.types.ts @@ -92,6 +92,7 @@ export interface AppStore extends TypedStore { darkMode: boolean; enableSpellchecking: boolean; enableTranslator: boolean; + useSelfSignedCertificates: boolean; fetchDataInterval: 4; get(key: string): any; getAppCacheSizeRequest: () => void; diff --git a/src/components/settings/settings/EditSettingsForm.tsx b/src/components/settings/settings/EditSettingsForm.tsx index 70887af0f..226dbac76 100644 --- a/src/components/settings/settings/EditSettingsForm.tsx +++ b/src/components/settings/settings/EditSettingsForm.tsx @@ -15,6 +15,7 @@ import Infobox from '../../ui/Infobox'; import { H1, H2, H3, H5 } from '../../ui/headline'; import { ferdiumVersion, + userDataCertsPath, userDataPath, userDataRecipesPath, } from '../../../environment-remote'; @@ -262,6 +263,20 @@ const messages = defineMessages({ id: 'settings.app.form.splitColumns', defaultMessage: 'Number of columns', }, + warningSelfSignedCertificates: { + id: 'settings.app.warningSelfSignedCertificates', + defaultMessage: + 'WARNING: Only enable this feature if you know what you are doing. Enabling this is a security risk and should only be used for testing purposes.', + }, + infoOpenCertificatesFolder: { + id: 'settings.app.infoOpenCertificatesFolder', + defaultMessage: + 'To install a certificate, click the button below to open the certificates folder and copy it into the folder. After that you can refresh the service (CTRL/CMD + R). To remove/uninstall, simply delete the certificate file and restart Ferdium.', + }, + buttonOpenFerdiumCertsFolder: { + id: 'settings.app.buttonOpenFerdiumCertsFolder', + defaultMessage: 'Open certificates folder', + }, }); const Hr = (): ReactElement => ( @@ -387,8 +402,12 @@ class EditSettingsForm extends Component { updateButtonLabelMessage = messages.updateStatusAvailable; } - const { lockingFeatureEnabled, scheduledDNDEnabled, reloadAfterResume } = - window['ferdium'].stores.settings.all.app; + const { + lockingFeatureEnabled, + scheduledDNDEnabled, + reloadAfterResume, + useSelfSignedCertificates, + } = window['ferdium'].stores.settings.all.app; let cacheSize; let notCleared; @@ -411,6 +430,7 @@ class EditSettingsForm extends Component { const profileFolder = userDataPath(); const recipeFolder = userDataRecipesPath(); + const certsFolder = userDataCertsPath(); return (
@@ -947,6 +967,43 @@ class EditSettingsForm extends Component { {intl.formatMessage(messages.appRestartRequired)}

+ + + {useSelfSignedCertificates && ( +
+

+ {intl.formatMessage(messages.infoOpenCertificatesFolder)} +

+
+
+
+ )} + +

+ {intl.formatMessage(messages.warningSelfSignedCertificates)} +

+
( + settings.all.app.useSelfSignedCertificates, + DEFAULT_APP_SETTINGS.useSelfSignedCertificates, + ), + default: DEFAULT_APP_SETTINGS.useSelfSignedCertificates, + type: 'checkbox', + }, spellcheckerLanguage: { label: intl.formatMessage(globalMessages.spellcheckerLanguage), value: ifUndefined( diff --git a/src/environment-remote.ts b/src/environment-remote.ts index 7662d69a0..346a75e97 100644 --- a/src/environment-remote.ts +++ b/src/environment-remote.ts @@ -48,6 +48,10 @@ export function userDataRecipesPath(...segments: string[]): string { return userDataPath('recipes', ...[segments].flat()); } +export function userDataCertsPath(...segments: string[]): string { + return userDataPath('certs', ...[segments].flat()); +} + const useLocalAPI = process.env.USE_LOCAL_API; export const useLiveAPI = process.env.USE_LIVE_API; diff --git a/src/helpers/certs-helpers.ts b/src/helpers/certs-helpers.ts new file mode 100644 index 000000000..a81e7e365 --- /dev/null +++ b/src/helpers/certs-helpers.ts @@ -0,0 +1,25 @@ +import { readdirSync, readFileSync, ensureDirSync } from 'fs-extra'; +import { join } from 'node:path'; +import { userDataCertsPath } from '../environment-remote'; + +export function removeNewLines(string: string) { + return string.replaceAll(/\r?\n|\r/g, ''); +} + +export function readCerts() { + const certsFolder = userDataCertsPath(); + + ensureDirSync(certsFolder); + + const certs: string[] = []; + + for (const file of readdirSync(certsFolder)) { + const cert = readFileSync(join(certsFolder, file), { + encoding: 'utf8', + flag: 'r', + }); + certs.push(removeNewLines(cert)); + } + + return certs; +} diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index eb8053fec..d2856e87a 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -188,6 +188,7 @@ "settings.account.yourLicense": "Your Ferdium License:", "settings.app.accentColorInfo": "Write your color choice in a CSS-compatible format. (Default: {defaultAccentColor} or clear the input field)", "settings.app.buttonInstallUpdate": "Restart & install update", + "settings.app.buttonOpenFerdiumCertsFolder": "Open certificates folder", "settings.app.buttonOpenFerdiumProfileFolder": "Open Profile folder", "settings.app.buttonOpenFerdiumServiceRecipesFolder": "Open Service Recipes folder", "settings.app.buttonOpenImportExport": "Import / Export", @@ -262,6 +263,7 @@ "settings.app.form.universalDarkMode": "Enable universal Dark Mode", "settings.app.form.useGrayscaleServices": "Use grayscale services", "settings.app.form.useHorizontalStyle": "Use horizontal style", + "settings.app.form.useSelfSignedCertificates": "Enable self-signed certificates", "settings.app.form.useTouchIdToUnlock": "Allow using TouchID to unlock Ferdium", "settings.app.form.wakeUpHibernationSplay": "Splay hibernate/wake cycles to reduce load", "settings.app.form.wakeUpHibernationStrategy": "Hibernation strategy after automatic wake up", @@ -276,6 +278,7 @@ "settings.app.headlineUpdates": "Updates", "settings.app.hibernateInfo": "By default, Ferdium will keep all your services open and loaded in the background so they are ready when you want to use them. Service Hibernation will unload your services after a specified amount. This is useful to save RAM or keeping services from slowing down your computer.", "settings.app.inactivityLockInfo": "Minutes of inactivity, after which Ferdium should automatically lock. Use 0 to disable", + "settings.app.infoOpenCertificatesFolder": "To install a certificate, click the button below to open the certificates folder and copy it into the folder. After that you can refresh the service (CTRL/CMD + R). To remove/uninstall, simply delete the certificate file and restart Ferdium.", "settings.app.lockInfo": "Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdium or lock Ferdium yourself using the lock symbol in the bottom left corner or the shortcut {lockShortcut}.", "settings.app.lockedPassword": "Password", "settings.app.lockedPasswordInfo": "Please make sure to set a password you'll remember.\nIf you loose this password, you will have to reinstall Ferdium.", @@ -306,6 +309,7 @@ "settings.app.updateStatusAvailable": "Update available, downloading...", "settings.app.updateStatusSearching": "Searching for updates...", "settings.app.updateStatusUpToDate": "You are using the latest version of Ferdium", + "settings.app.warningSelfSignedCertificates": "WARNING: Only enable this feature if you know what you are doing. Enabling this is a security risk and should only be used for testing purposes.", "settings.invite.headline": "Invite Friends", "settings.navigation.account": "Account", "settings.navigation.availableServices": "Available services", @@ -495,4 +499,4 @@ "workspaceDrawer.workspaceFeatureInfo": "

Ferdium Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.

You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.

", "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", "workspaces.switchingIndicator.switchingTo": "Switching to" -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index e466b4ab1..8419d5cf9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,6 +50,7 @@ import { openExternalUrl } from './helpers/url-helpers'; import userAgent from './helpers/userAgent-helpers'; import { translateTo } from './helpers/translation-helpers'; import { darkThemeGrayDarkest } from './themes/legacy'; +import { readCerts, removeNewLines } from './helpers/certs-helpers'; const debug = require('./preload-safe-debug')('Ferdium:App'); @@ -752,3 +753,41 @@ app.on('will-finish-launching', () => { }); }); }); + +app.on( + 'certificate-error', + (event, _webContents, _url, _error, certificate, callback) => { + // On certificate error we disable default behaviour (stop loading the page) + // and we then say "it is all fine - true" to the callback + event.preventDefault(); + + const useSelfSignedCertificates = + retrieveSettingValue( + 'useSelfSignedCertificates', + DEFAULT_APP_SETTINGS.useSelfSignedCertificates, + ) === true; + + // Check if the certificate is trusted + if (!useSelfSignedCertificates) { + callback(false); + return; + } + + const trustedCerts = readCerts(); + if (!trustedCerts) { + callback(false); + return; + } + + const isTrustedCert = trustedCerts.includes( + removeNewLines(certificate.data), + ); + + if (isTrustedCert) { + callback(true); + return; + } + + callback(false); + }, +); -- cgit v1.2.3-54-g00ecf