From 760099e226ceb7b5dd9e9e3fc7519a3e3e4b1706 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 Feb 2024 15:22:40 +0000 Subject: [PATCH] Improve loading of JSON files to avoid implicit behaviours of Node require (#1519) Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- src/electron-main.ts | 7 +++---- src/language-helper.ts | 3 ++- src/utils.ts | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/electron-main.ts b/src/electron-main.ts index 3110934..9059896 100644 --- a/src/electron-main.ts +++ b/src/electron-main.ts @@ -41,6 +41,7 @@ import { getProfileFromDeeplink, protocolInit } from "./protocol"; import { _t, AppLocalization } from "./language-helper"; import { setDisplayMediaCallback } from "./displayMediaCallback"; import { setupMacosTitleBar } from "./macos-titlebar"; +import { loadJsonFile } from "./utils"; const argv = minimist(process.argv, { alias: { help: "h" }, @@ -143,8 +144,7 @@ async function loadConfig(): Promise { const asarPath = await getAsarPath(); try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - global.vectorConfig = require(asarPath + "config.json"); + global.vectorConfig = loadJsonFile(asarPath, "config.json"); } catch (e) { // it would be nice to check the error code here and bail if the config // is unparsable, but we get MODULE_NOT_FOUND in the case of a missing @@ -155,8 +155,7 @@ async function loadConfig(): Promise { try { // Load local config and use it to override values from the one baked with the build - // eslint-disable-next-line @typescript-eslint/no-var-requires - const localConfig = require(path.join(app.getPath("userData"), "config.json")); + const localConfig = loadJsonFile(app.getPath("userData"), "config.json"); // If the local config has a homeserver defined, don't use the homeserver from the build // config. This is to avoid a problem where Riot thinks there are multiple homeservers diff --git a/src/language-helper.ts b/src/language-helper.ts index b705ab6..731fca1 100644 --- a/src/language-helper.ts +++ b/src/language-helper.ts @@ -19,6 +19,7 @@ import { TranslationKey as TKey } from "matrix-web-i18n"; import type Store from "electron-store"; import type EN from "./i18n/strings/en_EN.json"; +import { loadJsonFile } from "./utils"; const FALLBACK_LOCALE = "en"; @@ -105,7 +106,7 @@ export class AppLocalization { public fetchTranslationJson(locale: string): Record { try { console.log("Fetching translation json for locale: " + locale); - return require(`./i18n/strings/${this.denormalize(locale)}.json`); + return loadJsonFile(`./i18n/strings/${this.denormalize(locale)}.json`); } catch (e) { console.log(`Could not fetch translation json for locale: '${locale}'`, e); return {}; diff --git a/src/utils.ts b/src/utils.ts index 334721e..2aab292 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -15,6 +15,8 @@ limitations under the License. */ import crypto from "crypto"; +import fs from "node:fs"; +import path from "node:path"; export async function randomArray(size: number): Promise { return new Promise((resolve, reject) => { @@ -27,3 +29,20 @@ export async function randomArray(size: number): Promise { }); }); } + +type JsonValue = null | string | number; +type JsonArray = Array; +interface JsonObject { + [key: string]: JsonObject | JsonArray | JsonValue; +} +type Json = JsonArray | JsonObject; + +/** + * Synchronously load a JSON file from the local filesystem. + * Unlike `require`, will never execute any javascript in a loaded file. + * @param paths - An array of path segments which will be joined using the system's path delimiter. + */ +export function loadJsonFile(...paths: string[]): T { + const file = fs.readFileSync(path.join(...paths), { encoding: "utf-8" }); + return JSON.parse(file); +}