From 35c67c12d2186d06a46e64c7db4adc0218fe78ef Mon Sep 17 00:00:00 2001 From: hhhhhhhhhn Date: Sun, 19 Mar 2023 21:50:07 -0300 Subject: [PATCH 1/2] Stop using the shell to run commands This prevents RCE with $(...) syntax, and makes the regex escaping functions unnecessary, and the code slightly cleaner. --- index.js | 52 +++++++++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/index.js b/index.js index ea1cf3a..7cbcb1e 100644 --- a/index.js +++ b/index.js @@ -16,13 +16,10 @@ const semver = require('semver'); //const _7z = require('7zip-min'); const axios = require('axios') const platform = os.platform() -const shell = platform === 'win32' ? 'powershell.exe' : 'bash'; const L = require("./llama") const A = require("./alpaca") const TorrentDownloader = require("./torrent") const exists = s => new Promise(r=>fs.access(s, fs.constants.F_OK, e => r(!e))) -const escapeNewLine = (platform, arg) => platform === 'win32' ? arg.replaceAll(/\n/g, "\\n").replaceAll(/\r/g, "\\r") : arg -const escapeDoubleQuotes = (platform, arg) => platform === 'win32' ? arg.replaceAll(/"/g, '`"') : arg.replaceAll(/"/g, '\\"') const stripAnsi = (str) => { const pattern = [ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', @@ -32,15 +29,6 @@ const stripAnsi = (str) => { const regex = new RegExp(pattern, 'g') return str.replace(regex, ''); } -const winEscape = (str) => { - return str - .replaceAll(/\\n/g, "\n") - .replaceAll(/\\r/g, "\r") - .replaceAll(/\\t/g, "\t") - .replaceAll(/\\b/g, "\b") - .replaceAll(/\\f/g, "\f") - .replaceAll(/\\/g, "") -} class Dalai { constructor(home) { @@ -242,27 +230,25 @@ class Dalai { if (req.repeat_penalty) o.repeat_penalty = req.repeat_penalty if (typeof req.interactive !== "undefined") o.interactive = req.interactive - let chunks = [] + let args = [] for(let key in o) { - chunks.push(`--${key} ${escapeDoubleQuotes(platform, o[key].toString())}`) + args.push(`--${key}`, o[key].toString()) } - const escaped = escapeNewLine(platform, req.prompt) - const prompt = `"${escapeDoubleQuotes(platform, escaped)}"` - chunks.push(`-p ${prompt}`) + args.push("-p", req.prompt) const main_bin_path = platform === "win32" ? path.resolve(this.home, Core, "build", "Release", "main") : path.resolve(this.home, Core, "main") this.sessionBuffer = ""; this.bufferStarted = false; if (req.full) { - await this.exec(`${main_bin_path} ${chunks.join(" ")}`, this.cores[Core].home, cb) + await this.exec(main_bin_path, args, this.cores[Core].home, cb) } else { const startpattern = /.*sampling parameters:.*/g const endpattern = /.*mem per token.*/g let started = req.debug let ended = false let writeEnd = !req.skip_end - await this.exec(`${main_bin_path} ${chunks.join(" ")}`, this.cores[Core].home, (proc, msg) => { + await this.exec(main_bin_path, args, this.cores[Core].home, (proc, msg) => { if (endpattern.test(msg)) ended = true if (started && !ended) { this.buffer(req, msg, cb) @@ -446,19 +432,19 @@ class Dalai { // 3.2. Build tools if (platform === "linux") { // ubuntu debian - success = await this.exec("apt-get install build-essential python3-venv -y") + success = await this.exec("apt-get", ["install", "build-essential", "python3-venv", "-y"]) if (!success) { // fefdora - success = await this.exec("dnf install make automake gcc gcc-c++ kernel-devel python3-virtualenv -y") + success = await this.exec("dnf", ["install", "make", "automake", "gcc", "gcc-c++", "kernel-devel", "python3-virtualenv", "-y"]) } } else { // for win32 / darwin for(let root_pip_path of root_pip_paths) { - success = await this.exec(`${root_pip_path} install --user virtualenv`) + success = await this.exec(root_pip_path, ["install", "--user", "virtualenv"]) if (success) { break; } - success = await this.exec(`${root_pip_path} install virtualenv`) + success = await this.exec(root_pip_path, ["install", "virtualenv"]) if (success) { break; } @@ -473,7 +459,7 @@ class Dalai { const venv_path = path.join(this.home, "venv") for(let root_python_path of root_python_paths) { console.log("trying with", root_python_path) - let code = await this.exec(`${root_python_path} -m venv ${venv_path}`) + let code = await this.exec(root_python_path, ["-m", "venv", venv_path]) console.log({ code }) } /* @@ -488,24 +474,24 @@ class Dalai { const python_path = platform == "win32" ? path.join(venv_path, "Scripts", "python.exe") : path.join(venv_path, 'bin', 'python') // cmake (only on windows. the rest platforms use make) if (platform === "win32") { - success = await this.exec(`${pip_path} install cmake`) + success = await this.exec(pip_path, ["install", "cmake"]) if (!success) { throw new Error("cmake installation failed") return } } - success = await this.exec(`${pip_path} install --upgrade pip setuptools wheel`) + success = await this.exec(pip_path, ["install", "--upgrade", "pip", "setuptools", "wheel"]) if (!success) { - success = await this.exec(`${pip_path} install --user --upgrade pip setuptools wheel`) + success = await this.exec(pip_path, ["install", "--user", "--upgrade", "pip", "setuptools", "wheel"]) if (!success) { throw new Error("pip setuptools wheel upgrade failed") return } } - success = await this.exec(`${pip_path} install torch torchvision torchaudio sentencepiece numpy`) - //success = await this.exec(`${pip_path} install torch torchvision torchaudio sentencepiece numpy wget`) + success = await this.exec(pip_path, ["install", "torch", "torchvision", "torchaudio", "sentencepiece", "numpy"]) + //success = await this.exec(pip_path, ["install", "torch", "torchvision", "torchaudio", "sentencepiece", "numpy", "wget"]) if (!success) { - success = await this.exec(`${pip_path} install --user torch torchvision torchaudio sentencepiece numpy`) + success = await this.exec(pip_path, ["install", "--user", "torch", "torchvision", "torchaudio", "sentencepiece", "numpy"]) if (!success) { throw new Error("dependency installation failed") return @@ -552,15 +538,15 @@ class Dalai { throw e }); } - exec(cmd, cwd, cb) { + exec(cmd, args, cwd, cb) { return new Promise((resolve, reject) => { try { const config = Object.assign({}, this.config) if (cwd) { config.cwd = path.resolve(cwd) } - console.log(`exec: ${cmd} in ${config.cwd}`) - this.ptyProcess = pty.spawn(shell, [], config) + console.log(`exec: ${cmd} ${args.join(" ")} in ${config.cwd}`) + this.ptyProcess = pty.spawn(cmd, args, config) this.ptyProcess.onData((data) => { if (cb) { cb(this.ptyProcess, stripAnsi(data)) From 4515dd955fdad750cd0f2e8c64ac9a96e0437462 Mon Sep 17 00:00:00 2001 From: Myyk Seok Date: Wed, 29 Mar 2023 16:40:39 +0800 Subject: [PATCH 2/2] add back in winescape --- index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/index.js b/index.js index 7cbcb1e..3072a65 100644 --- a/index.js +++ b/index.js @@ -29,6 +29,15 @@ const stripAnsi = (str) => { const regex = new RegExp(pattern, 'g') return str.replace(regex, ''); } +const winEscape = (str) => { + return str + .replaceAll(/\\n/g, "\n") + .replaceAll(/\\r/g, "\r") + .replaceAll(/\\t/g, "\t") + .replaceAll(/\\b/g, "\b") + .replaceAll(/\\f/g, "\f") + .replaceAll(/\\/g, "") +} class Dalai { constructor(home) {