diff --git a/src/api/IBMi.ts b/src/api/IBMi.ts index 5c18f7701..6d357c36b 100644 --- a/src/api/IBMi.ts +++ b/src/api/IBMi.ts @@ -449,7 +449,7 @@ export default class IBMi { // and chmod follows the link, so detection must follow it too or it can never converge. // Returns 'error' when the output can't be parsed (empty stdout, ls error, banner noise). const getPermissions = async (path: string) => { - const out = (await this.sendCommand({ command: `ls -ldL "${path}" 2>/dev/null` })).stdout; + const out = (await this.sendCommand({ command: `ls -ldL "${Tools.escapePath(path, true)}" 2>/dev/null` })).stdout; return Tools.parseLsPermissions(out) ?? 'error'; }; @@ -458,7 +458,7 @@ export default class IBMi { // Check if .vscode directory exists and get its permissions const vscodeDir = `${defaultHomeDir}/.vscode`; const vscodeExistsResult = await this.sendCommand({ - command: `test -d ${vscodeDir} && echo "exists" || echo "notexists"` + command: `test -d "${Tools.escapePath(vscodeDir, true)}" && echo "exists" || echo "notexists"` }); const vscodeExists = vscodeExistsResult.stdout.trim() === 'exists'; const vscodePerms = vscodeExists ? await getPermissions(vscodeDir) : ''; @@ -894,7 +894,7 @@ export default class IBMi { if ((!quickConnect() || !cachedServerSettings?.pathChecked)) { const currentPaths = (await this.sendCommand({ command: "echo $PATH" })).stdout.split(":"); const bashrcFile = `${defaultHomeDir}/.bashrc`; - let bashrcExists = (await this.sendCommand({ command: `test -e ${bashrcFile}` })).code === 0; + let bashrcExists = (await this.sendCommand({ command: `test -e "${Tools.escapePath(bashrcFile, true)}"` })).code === 0; let reason; const requiredPaths = ["/QOpenSys/pkgs/bin", "/usr/bin", "/QOpenSys/usr/bin"] let missingPath; diff --git a/src/api/IBMiContent.ts b/src/api/IBMiContent.ts index 517d0ab3a..e72b9bde2 100644 --- a/src/api/IBMiContent.ts +++ b/src/api/IBMiContent.ts @@ -46,7 +46,7 @@ export default class IBMiContent { } private async getNotUTF8CCSID(attr: string, remotePath: string): Promise { - const result = await this.ibmi.sendCommand({ command: `${attr} "${remotePath}" CCSID` }); + const result = await this.ibmi.sendCommand({ command: `${attr} "${Tools.escapePath(remotePath, true)}" CCSID` }); if (result.code === 0) { //What's the point of converting 1208? let ccsid = result.stdout.trim(); @@ -1189,7 +1189,7 @@ export default class IBMiContent { async getSHA256FileHash(remoteFile: string) { //We use OPENSSL that is already build in inside os if (this.ibmi.remoteFeatures.openssl) { - const objhash = await this.ibmi.sendCommand({ command: `${this.ibmi.remoteFeatures.openssl} dgst -sha256 ${remoteFile} ` }); + const objhash = await this.ibmi.sendCommand({ command: `${this.ibmi.remoteFeatures.openssl} dgst -sha256 "${Tools.escapePath(remoteFile, true)}" ` }); if (objhash.code === 0) { return objhash.stdout.split(' ').pop(); } diff --git a/src/api/components/mapepire/index.ts b/src/api/components/mapepire/index.ts index 8727548a5..d9d112dee 100644 --- a/src/api/components/mapepire/index.ts +++ b/src/api/components/mapepire/index.ts @@ -5,6 +5,7 @@ import path from "path"; import { SemanticVersion } from "../../../typings"; import { getJavaHome } from "../../configuration/DebugConfiguration"; import IBMi from "../../IBMi"; +import { Tools } from "../../Tools"; import { IBMiComponent, SecureComponentState } from "../component"; import { SSHSQLJob } from "./sshSqlJob"; import { SERVER_FILE_PREFIX, SERVER_VERSION_FILE, VERSION } from "./version"; @@ -47,7 +48,7 @@ export class Mapepire implements IBMiComponent { return { status: "Installed", remoteSignature: Mapepire.SIGNATURE }; } - const remoteVersions = (await connection.sendCommand({ command: `/QOpenSys/usr/bin/find ${installDirectory} -type f -name ${SERVER_FILE_PREFIX}\\*` })) + const remoteVersions = (await connection.sendCommand({ command: `/QOpenSys/usr/bin/find "${Tools.escapePath(installDirectory, true)}" -type f -name ${SERVER_FILE_PREFIX}\\*` })) .stdout.split("\n") .map(line => line.trim().substring(2)) .map(line => new RegExp(`${SERVER_FILE_PREFIX}(\\d+)\\.(\\d+)\\.(\\d+)\\.jar$`).exec(line)) @@ -76,7 +77,7 @@ export class Mapepire implements IBMiComponent { } await connection.getContent().uploadFiles([{ local: this.localAssetPath, remote: this.installPath }]); - const result = await connection.sendCommand({ command: `chmod +x ${this.installPath}` }); + const result = await connection.sendCommand({ command: `chmod +x "${Tools.escapePath(this.installPath, true)}"` }); if (result.code !== 0) { throw `Failed to make Mapepire jar file executable: ${result.stderr}`; } @@ -91,7 +92,7 @@ export class Mapepire implements IBMiComponent { getInitCommand(javaHome: string): string | undefined { if (this.installPath) { - return `${javaHome ? path.posix.join(javaHome, `bin`, `java`) : 'java'} -Dos400.stdio.convert=N -jar ${this.installPath} --single` + return `${javaHome ? path.posix.join(javaHome, `bin`, `java`) : 'java'} -Dos400.stdio.convert=N -jar "${Tools.escapePath(this.installPath, true)}" --single` } } diff --git a/src/filesystems/local/deployment.ts b/src/filesystems/local/deployment.ts index 3202fc74e..0e0448bd5 100644 --- a/src/filesystems/local/deployment.ts +++ b/src/filesystems/local/deployment.ts @@ -146,7 +146,7 @@ export namespace Deployment { export async function createRemoteDirectory(remotePath: string) { return await getConnection().sendCommand({ - command: `mkdir -p "${remotePath}"` + command: `mkdir -p "${Tools.escapePath(remotePath, true)}"` }); } diff --git a/src/ui/connection.ts b/src/ui/connection.ts index fb82ef232..5abe0e349 100644 --- a/src/ui/connection.ts +++ b/src/ui/connection.ts @@ -1,5 +1,6 @@ import { CancellationToken, commands, env, Uri, window, workspace } from "vscode"; import IBMi, { ConnectionErrorCode, ConnectionMessageType } from "../api/IBMi"; +import { Tools } from "../api/Tools"; export function messageCallback(type: ConnectionMessageType, message: string) { switch (type) { @@ -48,7 +49,9 @@ export async function handleConnectionResults(connection: IBMi, error: Connectio detail: `Your home directory (${data}) does not exist, so Code for IBM i may not function correctly. Would you like to create this directory now?`, }, `Yes`)) { // Create home directory with 750 permissions and .vscode folder with 700 permissions - let mkHomeCmd = `mkdir -p ${data} && chown ${connection.currentUser.toLowerCase()} ${data} && chmod 0750 ${data} && mkdir -p ${data}/.vscode && chmod 0700 ${data}/.vscode`; + const escapedHome = Tools.escapePath(data, true); + const escapedHomeVscode = Tools.escapePath(`${data}/.vscode`, true); + let mkHomeCmd = `mkdir -p "${escapedHome}" && chown "${Tools.escapePath(connection.currentUser.toLowerCase(), true)}" "${escapedHome}" && chmod 0750 "${escapedHome}" && mkdir -p "${escapedHomeVscode}" && chmod 0700 "${escapedHomeVscode}"`; let mkHomeResult = await connection.sendCommand({ command: mkHomeCmd, directory: `.` }); if (0 === mkHomeResult.code) { return true; @@ -114,14 +117,17 @@ export async function handleConnectionResults(connection: IBMi, error: Connectio if (shouldUpdate) { let updateCmd = ''; + const escapedHomeDir = Tools.escapePath(homeDir, true); + const escapedVscodeDir = Tools.escapePath(`${homeDir}/.vscode`, true); + if (homePerms && homePerms !== '750') { - updateCmd += `chmod 0750 "${homeDir}"`; + updateCmd += `chmod 0750 "${escapedHomeDir}"`; } if (!vscodeExists) { - updateCmd += (updateCmd ? ' && ' : '') + `mkdir -p "${homeDir}/.vscode" && chmod 0700 "${homeDir}/.vscode"`; + updateCmd += (updateCmd ? ' && ' : '') + `mkdir -p "${escapedVscodeDir}" && chmod 0700 "${escapedVscodeDir}"`; } else if (vscodePerms && vscodePerms !== '700') { - updateCmd += (updateCmd ? ' && ' : '') + `chmod 0700 "${homeDir}/.vscode"`; + updateCmd += (updateCmd ? ' && ' : '') + `chmod 0700 "${escapedVscodeDir}"`; } if (updateCmd) { @@ -230,7 +236,9 @@ export async function handleConnectionResults(connection: IBMi, error: Connectio if (!bashrcExists) { // Add "/usr/bin" and "/QOpenSys/usr/bin" to the end of the path. This way we know that the user has // all the required paths, but we don't overwrite the priority of other items on their path. - const createBashrc = await connection.sendCommand({ command: `echo "# Generated by Code for IBM i\nexport PATH=/QOpenSys/pkgs/bin:\\$PATH:/QOpenSys/usr/bin:/usr/bin" >> ${bashrcFile} && chown ${connection.currentUser.toLowerCase()} ${bashrcFile} && chmod 755 ${bashrcFile}` }); + const escapedBashrcFile = Tools.escapePath(bashrcFile, true); + const escapedUser = Tools.escapePath(connection.currentUser.toLowerCase(), true); + const createBashrc = await connection.sendCommand({ command: `echo "# Generated by Code for IBM i\nexport PATH=/QOpenSys/pkgs/bin:\\$PATH:/QOpenSys/usr/bin:/usr/bin" >> "${escapedBashrcFile}" && chown "${escapedUser}" "${escapedBashrcFile}" && chmod 755 "${escapedBashrcFile}"` }); if (createBashrc.code !== 0) { window.showWarningMessage(`Error creating ${bashrcFile}):\n${createBashrc.stderr}.\n\n Code for IBM i may not function correctly. Please contact your system administrator.`, { modal: true }); }