Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tech] Enable sandboxing for ipcRenderer Processes #1783

Merged
merged 20 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1d7a8ad
Refactored all ipcRenderer calls into preload script
BrettCleary Sep 3, 2022
a2f465f
preload script working, context isolation enabled
BrettCleary Sep 6, 2022
b98c4a7
Cleaning up code, adding comments documenting design decisions
BrettCleary Sep 7, 2022
91068c8
preload path in vite.config plugin, checks if legendary folder exists…
BrettCleary Sep 7, 2022
c9a4d41
Merge branch 'main' of github.com:Heroic-Games-Launcher/HeroicGamesLa…
flavioislima Sep 7, 2022
7c0049f
[Fix] Check if Legendary's `metadata` folder exists before trying to …
CommandMC Sep 7, 2022
335a88e
Merge branch 'beta' into enableSandboxing
BrettCleary Sep 7, 2022
66d7657
refactor ipcRenderer calls in Tools component
BrettCleary Sep 9, 2022
1cc2224
fixing yarn codecheck issues with window.api calls
BrettCleary Sep 12, 2022
e96e7b2
fixed type issues window api install and rm wine
BrettCleary Sep 12, 2022
d8bf7b8
Merge remote-tracking branch 'upstream/beta' into enableSandboxing
BrettCleary Sep 12, 2022
67f0319
changed frontend error call to logError, removed superfluous comments
BrettCleary Sep 12, 2022
e41bdcd
installParams type now used in library api, library will refresh inst…
BrettCleary Sep 15, 2022
c60b240
Merge branch 'beta' into enableSandboxing
BrettCleary Sep 15, 2022
2ddd9d0
cleaning up after resolving conflicts with beta
BrettCleary Sep 15, 2022
2d2e15b
removing multiple imports
BrettCleary Sep 15, 2022
4268d3a
fixing wine progress issue, rm isDefault
BrettCleary Sep 16, 2022
7d96511
renaming handleProgressOf to handleProgressOfWineManager
BrettCleary Sep 16, 2022
906edaa
wine manager download progress fixes
BrettCleary Sep 18, 2022
c069fbd
typing wine api methods
BrettCleary Sep 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
"tslib": "^2.4.0"
},
"scripts": {
"dev": "vite",
"dev": "esbuild src/backend/preload.ts --platform=node --external:electron --bundle --outfile=build/electron/preload.js && vite",
BrettCleary marked this conversation as resolved.
Show resolved Hide resolved
"codecheck": "tsc --noEmit",
"test": "jest --noStackTrace",
"test-watch": "jest --watch --maxWorkers=25%",
Expand Down
46 changes: 46 additions & 0 deletions src/backend/api/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ipcRenderer } from 'electron'
import { Runner, InstallPlatform } from '../../common/types'

//src/frontend/helpers/index.ts
export const notify = (notification: string[]) =>
ipcRenderer.send('Notify', notification)
export const openLoginPage = () => ipcRenderer.send('openLoginPage')
export const openSidInfoPage = () => ipcRenderer.send('openSidInfoPage')
export const openSupportPage = () => ipcRenderer.send('openSupportPage')
export const quit = () => ipcRenderer.send('quit')
export const showAboutWindow = () => ipcRenderer.send('showAboutWindow')
export const openDiscordLink = () => ipcRenderer.send('openDiscordLink')
export const createNewWindow = (url: string) =>
ipcRenderer.send('createNewWindow', url)

//invoke
export const readConfig = async (file: string) =>
ipcRenderer.invoke('readConfig', file)
export const getPlatform = async () => ipcRenderer.invoke('getPlatform')
export const isLoggedIn = async () => ipcRenderer.invoke('isLoggedIn')
export const writeConfig = async (data: [appName: string, x: unknown]) =>
ipcRenderer.invoke('writeConfig', data)
export const kill = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('kill', appName, runner)
export const getUserInfo = async () => ipcRenderer.invoke('getUserInfo')
export const syncSaves = async (
args: [arg: string | undefined, path: string, appName: string, runner: string]
) => ipcRenderer.invoke('syncSaves', args)
export const getGameInfo = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('getGameInfo', appName, runner)
export const getGameSettings = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('getGameSettings', appName, runner)
export const getInstallInfo = async (
appName: string,
runner: Runner,
installPlatform?: InstallPlatform | string
) => ipcRenderer.invoke('getInstallInfo', appName, runner, installPlatform)
interface runWineCommand {
appName: string
runner: string
command: string
}
export const runWineCommandForGame = async (command: runWineCommand) =>
ipcRenderer.invoke('runWineCommandForGame', command)
export const requestSettings = async (appName: string) =>
ipcRenderer.invoke('requestSettings', appName)
15 changes: 15 additions & 0 deletions src/backend/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as Misc from './misc'
import * as Helpers from './helpers'
import * as Library from './library'
import * as Menu from './menu'
import * as Settings from './settings'
import * as Wine from './wine'

export default {
...Misc,
...Helpers,
...Library,
...Menu,
...Settings,
...Wine
}
57 changes: 57 additions & 0 deletions src/backend/api/library.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ipcRenderer } from 'electron'
import { Runner, InstallPlatform, LaunchParams } from '../../common/types'

//src/frontend/helpers/library.ts
export const removeFolder = (args: [path: string, folderName: string]) =>
ipcRenderer.send('removeFolder', args)

//invoke
export const openDialog = async (args: Electron.OpenDialogOptions) =>
ipcRenderer.invoke('openDialog', args)
interface InstallArgs {
appName: string
path: string
installDlcs: boolean
sdlList: string[]
installLanguage?: string
runner: Runner
platformToInstall?: InstallPlatform
}
export const install = async (args: InstallArgs) =>
ipcRenderer.invoke('install', args)
export const openMessageBox = async (args: Electron.MessageBoxOptions) =>
ipcRenderer.invoke('openMessageBox', args)
export const uninstall = async (
args: [appName: string, checkboxChecked: boolean, runner: Runner]
) => ipcRenderer.invoke('uninstall', args)
export const repair = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('repair', appName, runner)
export const launch = async (args: LaunchParams) =>
ipcRenderer.invoke('launch', args)
export const updateGame = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('updateGame', appName, runner)

interface ImportGameArgs {
appName: string
path: string
runner: Runner
}
export const importGame = async (args: ImportGameArgs) =>
ipcRenderer.invoke('importGame', args)

//main to renderer
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const handleSetGameStatus = (callback: any) =>
ipcRenderer.on('setGameStatus', callback)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const handleProgressOf = (version: string, callback: any) =>
ipcRenderer.on('progressOf' + version, callback)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const handleLaunchGame = (callback: any) =>
ipcRenderer.on('launchGame', callback)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const handleInstallGame = (callback: any) =>
ipcRenderer.on('installGame', callback)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const handleRefreshLibrary = (callback: any) =>
ipcRenderer.on('refreshLibrary', callback)
38 changes: 38 additions & 0 deletions src/backend/api/menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ipcRenderer } from 'electron'
import { Runner } from '../../common/types'

//src/frontend/screens/game/gamesubmenu
export const removeShortcut = (appName: string, runner: Runner) =>
ipcRenderer.send('removeShortcut', appName, runner)
export const addShortcut = (
appName: string,
runner: Runner,
fromMenu: boolean
) => ipcRenderer.send('addShortcut', appName, runner, fromMenu)
export const moveInstall = async (
args: [appName: string, path: string, runner: Runner]
) => ipcRenderer.invoke('moveInstall', args)
export const changeInstallPath = async (
args: [appName: string, path: string, runner: Runner]
) => ipcRenderer.invoke('changeInstallPath', args)
export const disableEosOverlay = async (appName: string) =>
ipcRenderer.invoke('disableEosOverlay', appName)
export const enableEosOverlay = async (appName: string) =>
ipcRenderer.invoke('enableEosOverlay', appName)
export const installEosOverlay = async () =>
ipcRenderer.invoke('installEosOverlay')
export const removeFromSteam = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('removeFromSteam', appName, runner)
export const addToSteam = async (
appName: string,
runner: Runner,
bkgDataURL: string,
bigPicDataURL: string
) =>
ipcRenderer.invoke('addToSteam', appName, runner, bkgDataURL, bigPicDataURL)
export const shortcutsExists = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('shortcutsExists', appName, runner)
export const isAddedToSteam = async (appName: string, runner: Runner) =>
ipcRenderer.invoke('isAddedToSteam', appName, runner)
export const isEosOverlayEnabled = async (appName?: string) =>
ipcRenderer.invoke('isEosOverlayEnabled', appName)
141 changes: 141 additions & 0 deletions src/backend/api/misc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { GOGCloudSavesLocation } from 'common/types/gog'
import { ipcRenderer } from 'electron'
import { Runner, Tools } from '../../common/types'

//src/components/ui/errorcomponent/index.tsx
export const clearCache = () => ipcRenderer.send('clearCache')
export const resetHeroic = () => ipcRenderer.send('resetHeroic')

//src/components/ui/languageselector
export const openWeblate = () => ipcRenderer.send('openWeblate')
export const changeLanguage = (newLanguage: string) =>
ipcRenderer.send('changeLanguage', newLanguage)

//src/components/ui/sidebar/components/heroicversion
export const openExternalUrl = (url: string) =>
ipcRenderer.send('openExternalUrl', url)
export const getHeroicVersion = async () =>
ipcRenderer.invoke('getHeroicVersion')
export const getLatestReleases = async () =>
ipcRenderer.invoke('getLatestReleases')

//src/components/ui/sidebar/components/sidebarlinks/index.tsx
export const openPatreonPage = () => ipcRenderer.send('openPatreonPage')
export const openKofiPage = () => ipcRenderer.send('openKofiPage')
export const isFullscreen = async () => ipcRenderer.invoke('isFullscreen')

//src/components/ui/webviewcontrols/index.tsx
export const openWebviewPage = (url: string) =>
ipcRenderer.send('openWebviewPage', url)

//src/frontend/state/globalstate
export const setZoomFactor = (zoom: string) =>
ipcRenderer.send('setZoomFactor', zoom)
export const frontendReady = () => ipcRenderer.send('frontendReady')
export const lock = () => ipcRenderer.send('lock')
export const unlock = () => ipcRenderer.send('unlock')
export const login = async (sid: string) => ipcRenderer.invoke('login', sid)
export const logoutLegendary = async () => ipcRenderer.invoke('logoutLegendary')
export const authGOG = async (token: string) =>
ipcRenderer.invoke('authGOG', token)
export const logoutGOG = async () => ipcRenderer.invoke('logoutGOG')
export const checkGameUpdates = async () =>
ipcRenderer.invoke('checkGameUpdates')
export const refreshWineVersionInfo = async (fetch?: boolean) =>
ipcRenderer.invoke('refreshWineVersionInfo', fetch)
export const refreshLibrary = async (
fullRefresh?: boolean,
library?: Runner | 'all'
) => ipcRenderer.invoke('refreshLibrary', fullRefresh, library)

//src/frontend/helpers/gamepads
export const gamepadAction = async (
args: [action: string, metadata: { elementTag: string; x: number; y: number }]
) => ipcRenderer.invoke('gamepadAction', args)

// misc
export const logError = (error: string) => ipcRenderer.send('logError', error)
export const logInfo = (info: string) => ipcRenderer.send('logInfo', info)
export const showConfigFileInFolder = (appName: string) =>
ipcRenderer.send('showConfigFileInFolder', appName)
export const openFolder = (installPath: string) =>
ipcRenderer.send('openFolder', installPath)
export const syncGOGSaves = async (
gogSaves: GOGCloudSavesLocation[],
appName: string,
arg: string
) => ipcRenderer.invoke('syncGOGSaves', gogSaves, appName, arg)
export const getFonts = async (reload: boolean) =>
ipcRenderer.invoke('getFonts', reload)
export const checkDiskSpace = async (installPath: string) =>
ipcRenderer.invoke('checkDiskSpace', installPath)
export const getGOGLinuxInstallersLangs = async (appName: string) =>
ipcRenderer.invoke('getGOGLinuxInstallersLangs', appName)
export const getAlternativeWine = async () =>
ipcRenderer.invoke('getAlternativeWine')
export const getGOGGameClientId = async (appName: string) =>
ipcRenderer.invoke('getGOGGameClientId', appName)
export const getShellPath = async (saveLocation: string) =>
ipcRenderer.invoke('getShellPath', saveLocation)
export const getRealPath = async (actualPath: string) =>
ipcRenderer.invoke('getRealPath', actualPath)
export const callTool = async (toolArgs: Tools) =>
ipcRenderer.invoke('callTool', toolArgs)
export const getAnticheatInfo = async (namespace: string) =>
ipcRenderer.invoke('getAnticheatInfo', namespace)

export const requestSettingsRemoveListeners = () =>
ipcRenderer.removeAllListeners('requestSettings')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const setGameStatusRemoveListener = (onGameStatusUpdate: any) =>
ipcRenderer.removeListener('setGameStatus', onGameStatusUpdate)

export const clipboardReadText = async () =>
ipcRenderer.invoke('clipboardReadText')

export const clipboardWriteText = async (text: string) =>
ipcRenderer.send('clipboardWriteText', text)

import Store from 'electron-store'
// FUTURE WORK
// here is how the store methods can be refactored
// in order to set nodeIntegration: false
// but converting sync methods to async propagates through frontend

// export const storeNew = async (
// name: string,
// options: Store.Options<Record<string, unknown>>
// ) => ipcRenderer.send('storeNew', name, options)

// export const storeSet = async (name: string, key: string, value?: unknown) =>
// ipcRenderer.send('storeSet', name, key, value)

// export const storeHas = async (name: string, key: string) =>
// ipcRenderer.invoke('storeHas', name, key)

// export const storeGet = async (name: string, key: string) =>
// ipcRenderer.invoke('storeGet', name, key)

interface StoreMap {
[key: string]: Store
}
const stores: StoreMap = {}

export const storeNew = function (
storeName: string,
options: Store.Options<Record<string, unknown>>
) {
stores[storeName] = new Store(options)
}

export const storeSet = (storeName: string, key: string, value?: unknown) =>
stores[storeName].set(key, value)

export const storeHas = (storeName: string, key: string) =>
stores[storeName].has(key)

export const storeGet = (
storeName: string,
key: string,
defaultValue?: unknown
) => stores[storeName].get(key, defaultValue)
38 changes: 38 additions & 0 deletions src/backend/api/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ipcRenderer } from 'electron'

//src/frontend/screens/settings/components/advancedsettings/index.tsx
export const getLegendaryVersion = async () =>
ipcRenderer.invoke('getLegendaryVersion')
export const getGogdlVersion = async () => ipcRenderer.invoke('getGogdlVersion')
export const getEosOverlayStatus = async () =>
ipcRenderer.invoke('getEosOverlayStatus')
export const getLatestEosOverlayVersion = async () =>
ipcRenderer.invoke('getLatestEosOverlayVersion')
export const removeEosOverlay = async () =>
ipcRenderer.invoke('removeEosOverlay')
export const cancelEosOverlayInstallOrUpdate = async () =>
ipcRenderer.invoke('cancelEosOverlayInstallOrUpdate')
export const updateEosOverlayInfo = async () =>
ipcRenderer.invoke('updateEosOverlayInfo')

//src/frontend/screens/settings/components/generalsettings/index.tsx
export const changeTrayColor = () => ipcRenderer.send('changeTrayColor')
export const getMaxCpus = async () => ipcRenderer.invoke('getMaxCpus')
export const showUpdateSetting = async () =>
ipcRenderer.invoke('showUpdateSetting')
export const egsSync = async (args: string) =>
ipcRenderer.invoke('egsSync', args)
export const showErrorBox = async (args: [title: string, message: string]) =>
ipcRenderer.invoke('showErrorBox', args)

//
export const showLogFileInFolder = (args: {
isDefault: boolean
appName: string
defaultLast?: boolean
}) => ipcRenderer.send('showLogFileInFolder', args)
export const getLogContent = async (args: {
isDefault: boolean
appName: string
defaultLast?: boolean
}) => ipcRenderer.invoke('getLogContent', args)
25 changes: 25 additions & 0 deletions src/backend/api/wine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ipcRenderer } from 'electron'
import { RuntimeName, WineVersionInfo } from '../../common/types'

//wine extensions
export const toggleDXVK = (
args: [wineArgs: { winePrefix: string; winePath: string }, action: string]
) => ipcRenderer.send('toggleDXVK', args)
export const toggleVKD3D = (
args: [wineArgs: { winePrefix: string; winePath: string }, action: string]
) => ipcRenderer.send('toggleVKD3D', args)
export const isFlatpak = async () => ipcRenderer.invoke('isFlatpak')
export const isRuntimeInstalled = async (runtime_name: RuntimeName) =>
ipcRenderer.invoke('isRuntimeInstalled', runtime_name)
export const downloadRuntime = async (runtime_name: RuntimeName) =>
ipcRenderer.invoke('downloadRuntime', runtime_name)

//winemanager/components/wineitem
export const showItemInFolder = (installDir: string) =>
ipcRenderer.send('showItemInFolder', installDir)
export const abortWineInstallation = (version: string) =>
ipcRenderer.send('abortWineInstallation', version)
export const installWineVersion = async (release: WineVersionInfo) =>
ipcRenderer.invoke('installWineVersion', release)
export const removeWineVersion = async (release: WineVersionInfo) =>
ipcRenderer.invoke('removeWineVersion', release)