fix potential sandbox escape via custom vencordDir
This commit is contained in:
parent
ec3d83f7ca
commit
1f12d270ec
9 changed files with 84 additions and 20 deletions
|
@ -37,7 +37,8 @@ if (existsSync(LEGACY_DATA_DIR)) {
|
|||
console.error("Migration failed", e);
|
||||
}
|
||||
}
|
||||
app.setPath("sessionData", join(DATA_DIR, "sessionData"));
|
||||
const SESSION_DATA_DIR = join(DATA_DIR, "sessionData");
|
||||
app.setPath("sessionData", SESSION_DATA_DIR);
|
||||
|
||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
||||
|
@ -47,7 +48,8 @@ export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes");
|
|||
// needs to be inline require because of circular dependency
|
||||
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
|
||||
export const VENCORD_FILES_DIR =
|
||||
(require("./settings") as typeof import("./settings")).Settings.store.vencordDir || join(DATA_DIR, "vencordDist");
|
||||
(require("./settings") as typeof import("./settings")).State.store.vencordDir ||
|
||||
join(SESSION_DATA_DIR, "vencordFiles");
|
||||
|
||||
export const USER_AGENT = `Vesktop/${app.getVersion()} (https://github.com/Vencord/Vesktop)`;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import { setBadgeCount } from "./appBadge";
|
|||
import { autoStart } from "./autoStart";
|
||||
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
|
||||
import { mainWin } from "./mainWindow";
|
||||
import { Settings } from "./settings";
|
||||
import { Settings, State } from "./settings";
|
||||
import { handle, handleSync } from "./utils/ipcWrappers";
|
||||
import { PopoutWindows } from "./utils/popout";
|
||||
import { isDeckGameMode, showGamePage } from "./utils/steamOS";
|
||||
|
@ -105,7 +105,15 @@ handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
|
|||
e.sender.session.addWordToSpellCheckerDictionary(word);
|
||||
});
|
||||
|
||||
handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||
handleSync(IpcEvents.GET_VENCORD_DIR, e => (e.returnValue = State.store.vencordDir));
|
||||
|
||||
handle(IpcEvents.SELECT_VENCORD_DIR, async (_e, value?: null) => {
|
||||
console.log(value);
|
||||
if (value === null) {
|
||||
delete State.store.vencordDir;
|
||||
return "ok";
|
||||
}
|
||||
|
||||
const res = await dialog.showOpenDialog(mainWin!, {
|
||||
properties: ["openDirectory"]
|
||||
});
|
||||
|
@ -114,7 +122,9 @@ handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
|||
const dir = res.filePaths[0];
|
||||
if (!isValidVencordInstall(dir)) return "invalid";
|
||||
|
||||
return dir;
|
||||
State.store.vencordDir = dir;
|
||||
|
||||
return "ok";
|
||||
});
|
||||
|
||||
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { existsSync, mkdirSync } from "fs";
|
||||
import { mkdirSync } from "fs";
|
||||
import { access, constants as FsConstants } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
import { USER_AGENT, VENCORD_FILES_DIR } from "../constants";
|
||||
|
@ -56,12 +57,18 @@ export async function downloadVencordFiles() {
|
|||
);
|
||||
}
|
||||
|
||||
export function isValidVencordInstall(dir: string) {
|
||||
return FILES_TO_DOWNLOAD.every(f => existsSync(join(dir, f)));
|
||||
const existsAsync = (path: string) =>
|
||||
access(path, FsConstants.F_OK)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
export async function isValidVencordInstall(dir: string) {
|
||||
return Promise.all(FILES_TO_DOWNLOAD.map(f => existsAsync(join(dir, f)))).then(arr => !arr.includes(false));
|
||||
}
|
||||
|
||||
export async function ensureVencordFiles() {
|
||||
if (isValidVencordInstall(VENCORD_FILES_DIR)) return;
|
||||
if (await isValidVencordInstall(VENCORD_FILES_DIR)) return;
|
||||
|
||||
mkdirSync(VENCORD_FILES_DIR, { recursive: true });
|
||||
|
||||
await downloadVencordFiles();
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { Node } from "@vencord/venmic";
|
||||
import { ipcRenderer } from "electron";
|
||||
import type { Settings } from "shared/settings";
|
||||
import type { LiteralUnion } from "type-fest";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { invoke, sendSync } from "./typedIpc";
|
||||
|
@ -34,7 +33,8 @@ export const VesktopNative = {
|
|||
},
|
||||
fileManager: {
|
||||
showItemInFolder: (path: string) => invoke<void>(IpcEvents.SHOW_ITEM_IN_FOLDER, path),
|
||||
selectVencordDir: () => invoke<LiteralUnion<"cancelled" | "invalid", string>>(IpcEvents.SELECT_VENCORD_DIR)
|
||||
getVencordDir: () => sendSync<string | undefined>(IpcEvents.GET_VENCORD_DIR),
|
||||
selectVencordDir: (value?: null) => invoke<"cancelled" | "invalid" | "ok">(IpcEvents.SELECT_VENCORD_DIR, value)
|
||||
},
|
||||
settings: {
|
||||
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
|
||||
|
|
|
@ -4,24 +4,28 @@
|
|||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { useForceUpdater } from "@vencord/types/utils";
|
||||
import { Button, Forms, Toasts } from "@vencord/types/webpack/common";
|
||||
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||
const forceUpdate = useForceUpdater();
|
||||
const vencordDir = VesktopNative.fileManager.getVencordDir();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Forms.FormText>
|
||||
Vencord files are loaded from{" "}
|
||||
{settings.vencordDir ? (
|
||||
{vencordDir ? (
|
||||
<a
|
||||
href="about:blank"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
VesktopNative.fileManager.showItemInFolder(settings.vencordDir!);
|
||||
VesktopNative.fileManager.showItemInFolder(vencordDir!);
|
||||
}}
|
||||
>
|
||||
{settings.vencordDir}
|
||||
{vencordDir}
|
||||
</a>
|
||||
) : (
|
||||
"the default location"
|
||||
|
@ -34,7 +38,14 @@ export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
|||
const choice = await VesktopNative.fileManager.selectVencordDir();
|
||||
switch (choice) {
|
||||
case "cancelled":
|
||||
return;
|
||||
break;
|
||||
case "ok":
|
||||
Toasts.show({
|
||||
message: "Vencord install changed. Fully restart Vesktop to apply.",
|
||||
id: Toasts.genId(),
|
||||
type: Toasts.Type.SUCCESS
|
||||
});
|
||||
break;
|
||||
case "invalid":
|
||||
Toasts.show({
|
||||
message:
|
||||
|
@ -42,9 +53,9 @@ export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
|||
id: Toasts.genId(),
|
||||
type: Toasts.Type.FAILURE
|
||||
});
|
||||
return;
|
||||
break;
|
||||
}
|
||||
settings.vencordDir = choice;
|
||||
forceUpdate();
|
||||
}}
|
||||
>
|
||||
Change
|
||||
|
@ -52,7 +63,10 @@ export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
|||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.RED}
|
||||
onClick={() => (settings.vencordDir = void 0)}
|
||||
onClick={async () => {
|
||||
await VesktopNative.fileManager.selectVencordDir(null);
|
||||
forceUpdate();
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
|
|
|
@ -13,7 +13,7 @@ console.log("read if cute :3");
|
|||
|
||||
export * as Components from "./components";
|
||||
import { findByPropsLazy, onceReady } from "@vencord/types/webpack";
|
||||
import { FluxDispatcher } from "@vencord/types/webpack/common";
|
||||
import { Alerts, FluxDispatcher } from "@vencord/types/webpack/common";
|
||||
|
||||
import SettingsUi from "./components/settings/Settings";
|
||||
import { Settings } from "./settings";
|
||||
|
@ -59,3 +59,19 @@ VesktopNative.arrpc.onActivity(async data => {
|
|||
|
||||
arRPC.handleEvent(new MessageEvent("message", { data }));
|
||||
});
|
||||
|
||||
// TODO: remove soon
|
||||
const vencordDir = "vencordDir" as keyof typeof Settings.store;
|
||||
if (Settings.store[vencordDir]) {
|
||||
onceReady.then(() =>
|
||||
setTimeout(
|
||||
() =>
|
||||
Alerts.show({
|
||||
title: "Custom Vencord Location",
|
||||
body: "Due to changes in Vesktop, your custom Vencord location had to bee reset. Please set it again in the settings.",
|
||||
onConfirm: () => delete Settings.store[vencordDir]
|
||||
}),
|
||||
5000
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ export const enum IpcEvents {
|
|||
GET_SETTINGS = "VCD_GET_SETTINGS",
|
||||
SET_SETTINGS = "VCD_SET_SETTINGS",
|
||||
|
||||
GET_VENCORD_DIR = "VCD_GET_VENCORD_DIR",
|
||||
SELECT_VENCORD_DIR = "VCD_SELECT_VENCORD_DIR",
|
||||
|
||||
UPDATER_GET_DATA = "VCD_UPDATER_GET_DATA",
|
||||
|
|
3
src/shared/settings.d.ts
vendored
3
src/shared/settings.d.ts
vendored
|
@ -8,7 +8,6 @@ import type { Rectangle } from "electron";
|
|||
|
||||
export interface Settings {
|
||||
discordBranch?: "stable" | "canary" | "ptb";
|
||||
vencordDir?: string;
|
||||
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
||||
tray?: boolean;
|
||||
minimizeToTray?: boolean;
|
||||
|
@ -54,4 +53,6 @@ export interface State {
|
|||
firstLaunch?: boolean;
|
||||
|
||||
steamOSLayoutVersion?: number;
|
||||
|
||||
vencordDir?: string;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,19 @@ export class SettingsStore<T extends object> {
|
|||
self.pathListeners.get(setPath)?.forEach(cb => cb(value));
|
||||
|
||||
return true;
|
||||
},
|
||||
deleteProperty(target, key: string) {
|
||||
if (!(key in target)) return true;
|
||||
|
||||
const res = Reflect.deleteProperty(target, key);
|
||||
if (!res) return false;
|
||||
|
||||
const setPath = `${path}${path && "."}${key}`;
|
||||
|
||||
self.globalListeners.forEach(cb => cb(root, setPath));
|
||||
self.pathListeners.get(setPath)?.forEach(cb => cb(undefined));
|
||||
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue