UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

269 lines (268 loc) 10.5 kB
"use strict"; /** * Electron Preload Script * * This script runs in a privileged context before the renderer process loads. * It exposes a safe API to the renderer via contextBridge. * * SECURITY: This script acts as a bridge between the main process (Node.js) * and the renderer process (web). It carefully validates all operations * before allowing them. * * NOTE: This file is compiled separately with contextIsolation support. * It uses CommonJS (require) because Electron's preload scripts don't * support ESM in the same way as the main process. */ Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const _allowedExtensions = [ "js", "ts", "mjs", "json", "md", "png", "jpg", "jpeg", "lang", "fsb", "map", "ogg", "flac", "hdr", "psd", "env", "gif", "wav", "tga", "env", "mjs", "wlist", "brarchive", "nbt", "webm", "svg", "otf", "ttf", "bin", "obj", "pdn", "zip", "py", "h", "fontdata", "properties", "cartobackup", "mctbackup", "", "mcstructure", "mcworld", "mcproject", "map", "js.map", "mctemplate", "material", "vertex", "md", "geometry", "fragment", "mcfunction", "mcaddon", "mcpack", "html", "dat", "dat_old", "txt", "ldb", "log", "in", "cmake", ]; const _allowedExecutableExtensions = ["mcstructure", "mcworld", "mctemplate", "mcaddon"]; function _getTypeFromName(name) { const nameW = name.trim().toLowerCase(); const lastBackslash = nameW.lastIndexOf("\\"); const lastSlash = nameW.lastIndexOf("/"); const lastPeriod = nameW.lastIndexOf("."); if (lastPeriod < 0 || lastPeriod < lastSlash || lastPeriod < lastBackslash) { return ""; } return nameW.substring(lastPeriod + 1, nameW.length); } function _canonicalizePathForValidation(path) { if ((path[0] !== "<" || path[5] !== ">") && !path.startsWith("<pt_")) { throw new Error("PLD: Unsupported canon path: " + path); } if (path.startsWith("<EDUP>") || path.startsWith("<EDUR>") || path.startsWith("<BDRK>") || path.startsWith("<BDPV>") || path.startsWith("<MCPE>") || path.startsWith("<DOCP>")) { path = path.substring(6); } else if (path.startsWith("<pt_")) { const endGreater = path.indexOf(">", 4); if (endGreater > 4) { path = path.substring(endGreater + 1); } } else { throw new Error("PLD: Unsupported canon path A: " + path); } return path; } function _validateFolderPath(path) { path = _canonicalizePathForValidation(path); // banned character combos if (path.indexOf("..") >= 0 || path.indexOf("\\\\") >= 0 || path.indexOf("//") >= 0) { throw new Error("Unsupported path combinations: " + path); } if (path.lastIndexOf(":") >= 2) { throw new Error("Unsupported drive location: " + path); } } function _validateDoubleFolderPath(path) { const pathArr = path.split("|"); if (pathArr.length !== 2) { throw new Error("Unsupported double folder path: " + path); } _validateFolderPath(pathArr[0]); _validateFolderPath(pathArr[1]); } function _validateFilePath(path) { _validateFolderPath(path); const extension = _getTypeFromName(path); if (!_allowedExtensions.includes(extension)) { throw new Error("PLD: Unsupported file type: " + path); } } function _validateExecutableFilePath(path) { _validateFolderPath(path); const extension = _getTypeFromName(path); if (!_allowedExecutableExtensions.includes(extension)) { throw new Error("PLD: Unsupported executable file type: " + path); } } // Valid channels for Agent IPC (uses invoke pattern directly) const _agentChannels = [ "agent:start", "agent:stop", "agent:createSession", "agent:send", "agent:abort", "agent:destroySession", "agent:getAuthStatus", "agent:listModels", "agent:updateContext", ]; electron_1.contextBridge.exposeInMainWorld("api", { // Direct invoke for Agent channels (cleaner API) invoke: (channel, data) => { if (_agentChannels.includes(channel)) { return electron_1.ipcRenderer.invoke(channel, data); } throw new Error("PLD: Invalid invoke channel: " + channel); }, send: (channel, commandName, data) => { const validChannels = ["appweb"]; if (validChannels.includes(channel)) { const pipe = commandName.indexOf("|"); let position = -1; if (pipe >= 0) { position = parseInt(commandName.substring(pipe + 1, commandName.length)); commandName = commandName.substring(0, pipe); } switch (commandName) { case "asyncopenFolder": electron_1.ipcRenderer.invoke("asyncselectDirectory", position + "|" + data); return; case "asyncconvertFile": return electron_1.ipcRenderer.invoke("asyncconvertFile", position + "|" + data); case "asyncstartWebSocketServer": return electron_1.ipcRenderer.invoke("asyncstartWebSocketServer", position + "|" + data); case "asyncstopWebSocketServer": return electron_1.ipcRenderer.invoke("asyncstopWebSocketServer", position + "|" + data); case "asyncstartDedicatedServer": return electron_1.ipcRenderer.invoke("asyncstartDedicatedServer", position + "|" + data); case "asyncstopDedicatedServer": return electron_1.ipcRenderer.invoke("asyncstopDedicatedServer", position + "|" + data); case "asyncdedicatedServerCommand": return electron_1.ipcRenderer.invoke("asyncdedicatedServerCommand", position + "|" + data); case "asyncwebSocketCommand": return electron_1.ipcRenderer.invoke("asyncwebSocketCommand", position + "|" + data); case "asyncshellOpenPath": _validateExecutableFilePath(data); return electron_1.ipcRenderer.invoke("asyncshellOpenPath", position + "|" + data); case "asyncshellOpenFolderInExplorer": return electron_1.ipcRenderer.invoke("asyncshellOpenFolderInExplorer", position + "|" + data); case "asyncminecraftShell": return electron_1.ipcRenderer.invoke("asyncminecraftShell", position + "|" + data); case "asyncshellRecycleItem": _validateFilePath(data); return electron_1.ipcRenderer.invoke("asyncshellRecycleItem", position + "|" + data); case "asyncreloadMct": return electron_1.ipcRenderer.invoke("asyncreloadMct", position + "|" + data); case "asyncgetDedicatedServerProjectDir": return electron_1.ipcRenderer.invoke("asyncgetDedicatedServerProjectDir", position + "|" + data); case "asyncgetDedicatedServerStatus": return electron_1.ipcRenderer.invoke("asyncgetDedicatedServerStatus", position + "|" + data); case "asyncgetDedicatedServerWorldDir": return electron_1.ipcRenderer.invoke("asyncgetDedicatedServerWorldDir", position + "|" + data); case "asyncgetMinecraftGameProjectDeployDir": return electron_1.ipcRenderer.invoke("asyncgetMinecraftGameProjectDeployDir", position + "|" + data); case "asyncgetMinecraftGameWorldDeployDir": return electron_1.ipcRenderer.invoke("asyncgetMinecraftGameWorldDeployDir", position + "|" + data); case "asyncwindowClose": case "asynclogToConsole": case "asyncwindowRestore": case "asyncwindowMinimize": case "asyncwindowMove": case "asyncwindowMaximize": case "asyncwindowUpdate": case "asyncwindowLeftSide": case "asyncwindowRightSide": case "asyncupdateIAgree": case "asyncgetWindowState": case "asyncgetPlatform": case "asyncgetDirname": case "asyncgetContentSources": case "asynccontentSourceLogin": return electron_1.ipcRenderer.invoke(commandName, position + "|" + data); case "getIsDev": return electron_1.ipcRenderer.invoke("getIsDev", data); case "asyncfsRenameFolder": _validateDoubleFolderPath(data); return electron_1.ipcRenderer.invoke(commandName, position + "|" + data); case "asyncfsDeleteFolder": _validateFolderPath(data); return electron_1.ipcRenderer.invoke(commandName, position + "|" + data); case "asyncfsExists": case "bsyncfsReadFile": case "asyncfsReadUtf8File": _validateFilePath(data); return electron_1.ipcRenderer.invoke(commandName, position + "|" + data); case "asyncfsFolderExists": case "asyncfsRootStorageExists": case "asyncfsMkdir": case "asyncfsReaddir": case "asyncfsStat": _validateFolderPath(data); return electron_1.ipcRenderer.invoke(commandName, position + "|" + data); case "asyncfsWriteFile": case "asyncfsWriteUtf8File": const writeFilePath = data.path; _validateFilePath(writeFilePath); return electron_1.ipcRenderer.invoke(commandName, position + "|" + data.path + "|" + data.content); default: throw new Error("PLD: Unknown command: " + commandName); } } }, receive: (channel, func) => { const validChannels = ["appsvc", "agent:event", "error:fatal"]; if (validChannels.includes(channel)) { electron_1.ipcRenderer.on(channel, (_event, ...args) => func(...args)); } }, });