diff --git a/lib/utils.js b/lib/utils.js index 66166db..dc6159f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { util, net } from 'appium/support'; +import { net } from 'appium/support'; import { promisify } from 'node:util'; import { exec } from 'node:child_process'; import B from 'bluebird'; @@ -21,7 +21,7 @@ export async function shellExec(cmd, args = [], opts = {}) { const { timeoutMs = 60 * 1000 * 5 } = opts; - const fullCmd = util.quote([cmd, ...args]); + const fullCmd = [cmd, ...args].map(escapeWindowsArg).join(' '); const { stdout, stderr } = await B.resolve(execAsync(fullCmd, opts)) .timeout(timeoutMs, `The command '${fullCmd}' timed out after ${timeoutMs}ms`); return { @@ -30,6 +30,27 @@ export async function shellExec(cmd, args = [], opts = {}) { }; } +/** + * Escapes a string to be used as a Windows command line argument + * + * @param {string} arg + * @returns {string} + */ +function escapeWindowsArg(arg) { + if (!arg) { + return '""'; + } + + const needsQuotes = /[\s"]/g.test(arg); + if (!needsQuotes) { + return arg; + } + + // Escape double quotes and backslashes before quotes + const escaped = arg.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, '$1$1'); + return `"${escaped}"`; +} + /** * * @param {string} srcUrl