From aa67a1c3359cd4694a455425b8faf854d52d3d0b Mon Sep 17 00:00:00 2001 From: Zvi Grinberg Date: Thu, 13 Jun 2024 16:32:58 +0300 Subject: [PATCH] fix: handle spaces in package manager binaries paths Signed-off-by: Zvi Grinberg --- src/providers/golang_gomodules.js | 10 +++++----- src/providers/java_gradle.js | 6 +++--- src/providers/java_maven.js | 10 +++++----- src/providers/javascript_npm.js | 6 +++--- src/providers/python_controller.js | 18 +++++++++--------- src/providers/python_pip.js | 15 ++++++++++----- 6 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/providers/golang_gomodules.js b/src/providers/golang_gomodules.js index 45bed5a..4e76e42 100644 --- a/src/providers/golang_gomodules.js +++ b/src/providers/golang_gomodules.js @@ -3,7 +3,7 @@ import { execSync } from "node:child_process" import fs from 'node:fs' import os from "node:os"; import {EOL} from "os"; -import {getCustom, getCustomPath} from "../tools.js"; +import {getCustom, getCustomPath, handleSpacesInPath} from "../tools.js"; import path from 'node:path' import Sbom from '../sbom.js' import {PackageURL} from 'packageurl-js' @@ -72,7 +72,7 @@ function provideComponent(data, opts = {}) { function getGoGraphCommand(goBin) { - return `${goBin} mod graph `; + return `${handleSpacesInPath(goBin)} mod graph `; } /** @@ -273,7 +273,7 @@ function getSBOM(manifest, opts = {}, includeTransitive) { // get custom goBin path let goBin = getCustomPath('go', opts) // verify goBin is accessible - execSync(`${goBin} version`, err => { + execSync(`${handleSpacesInPath(goBin)} version`, err => { if (err) { throw new Error('go binary is not accessible') } @@ -380,8 +380,8 @@ function toPurl(dependency, delimiter, qualifiers) { function getFinalPackagesVersionsForModule(rows,manifestPath,goBin) { let manifestDir = path.dirname(manifestPath) let options = {cwd: manifestDir} - execSync(`${goBin} mod download`, options) - let finalVersionsForAllModules = execSync(`${goBin} list -m all`, options).toString() + execSync(`${handleSpacesInPath(goBin)} mod download`, options) + let finalVersionsForAllModules = execSync(`${handleSpacesInPath(goBin)} list -m all`, options).toString() let finalVersionModules = new Map() finalVersionsForAllModules.split(EOL).filter(string => string.trim()!== "") .filter(string => string.trim().split(" ").length === 2) diff --git a/src/providers/java_gradle.js b/src/providers/java_gradle.js index bc64fda..83c2bc6 100644 --- a/src/providers/java_gradle.js +++ b/src/providers/java_gradle.js @@ -1,5 +1,5 @@ import fs from 'node:fs' -import {getCustomPath} from "../tools.js"; +import {getCustomPath, handleSpacesInPath} from "../tools.js"; import path from 'node:path' import Sbom from '../sbom.js' import {EOL} from 'os' @@ -176,7 +176,7 @@ export default class Java_gradle extends Base_java { let gradle = getCustomPath("gradle", opts); let properties try { - properties = this._invokeCommandGetOutput(`${gradle} properties`, path.dirname(manifestPath)) + properties = this._invokeCommandGetOutput(`${handleSpacesInPath(gradle)} properties`, path.dirname(manifestPath)) } catch (e) { throw new Error(`Couldn't get properties of build.gradle file , Error message returned from gradle binary => ${EOL} ${e.getMessage}`) } @@ -221,7 +221,7 @@ export default class Java_gradle extends Base_java { let commandResult gradle = getCustomPath("gradle") try { - commandResult = this._invokeCommandGetOutput(`${gradle} dependencies`,path.dirname(manifest)) + commandResult = this._invokeCommandGetOutput(`${handleSpacesInPath(gradle)} dependencies`,path.dirname(manifest)) } catch (e) { throw new Error(`Couldn't run gradle dependencies command, error message returned from gradle binary => ${EOL} ${e.getMessage}`) } diff --git a/src/providers/java_maven.js b/src/providers/java_maven.js index a496334..950eee1 100644 --- a/src/providers/java_maven.js +++ b/src/providers/java_maven.js @@ -72,14 +72,14 @@ export default class Java_maven extends Base_java { // get custom maven path let mvn = getCustomPath('mvn', opts) // verify maven is accessible - this._invokeCommand(`${mvn} --version`,'mvn is not accessible') + this._invokeCommand(`${handleSpacesInPath(mvn)} --version`,'mvn is not accessible') // clean maven target - this._invokeCommand(`${mvn} -q clean -f ${handleSpacesInPath(manifest)}`,'failed cleaning maven target') + this._invokeCommand(`${handleSpacesInPath(mvn)} -q clean -f ${handleSpacesInPath(manifest)}`,'failed cleaning maven target') // create dependency graph in a temp file let tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'exhort_')) let tmpDepTree = path.join(tmpDir, 'mvn_deptree.txt') // build initial command (dot outputType is not available for verbose mode) - let depTreeCmd = `${mvn} -q org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree -Dverbose -DoutputType=text -DoutputFile=${handleSpacesInPath(tmpDepTree)} -f ${handleSpacesInPath(manifest)}` + let depTreeCmd = `${handleSpacesInPath(mvn)} -q org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree -Dverbose -DoutputType=text -DoutputFile=${handleSpacesInPath(tmpDepTree)} -f ${handleSpacesInPath(manifest)}` // exclude ignored dependencies, exclude format is groupId:artifactId:scope:version. // version and scope are marked as '*' if not specified (we do not use scope yet) let ignoredDeps = new Array() @@ -132,7 +132,7 @@ export default class Java_maven extends Base_java { // get custom maven path let mvn = getCustomPath('mvn', opts) // verify maven is accessible - this._invokeCommand(`${mvn} --version`,'mvn is not accessible') + this._invokeCommand(`${handleSpacesInPath(mvn)} --version`,'mvn is not accessible') // create temp files for pom and effective pom let tmpDir let tmpEffectivePom @@ -150,7 +150,7 @@ export default class Java_maven extends Base_java { } // create effective pom and save to temp file - this._invokeCommand(`${mvn} -q help:effective-pom -Doutput=${handleSpacesInPath(tmpEffectivePom)} -f ${handleSpacesInPath(targetPom)}`,'failed creating maven effective pom') + this._invokeCommand(`${handleSpacesInPath(mvn)} -q help:effective-pom -Doutput=${handleSpacesInPath(tmpEffectivePom)} -f ${handleSpacesInPath(targetPom)}`,'failed creating maven effective pom') // iterate over all dependencies in original pom and collect all ignored ones let ignored = this.#getDependencies(targetPom).filter(d => d.ignore) // iterate over all dependencies and create a package for every non-ignored one diff --git a/src/providers/javascript_npm.js b/src/providers/javascript_npm.js index b506f5e..a46f154 100644 --- a/src/providers/javascript_npm.js +++ b/src/providers/javascript_npm.js @@ -16,14 +16,14 @@ export var npmInteractions = { return npmOutput; }, version: function checkNpmVersion(npm) { - execSync(`${npm} --version`, err => { + execSync(`${handleSpacesInPath(npm)} --version`, err => { if (err) { throw new Error('npm is not accessible') } }) }, createPackageLock: function createPackageLock(npm, manifestDir) { - execSync(`${npm} i --package-lock-only --prefix ${handleSpacesInPath(manifestDir)}`, err => { + execSync(`${handleSpacesInPath(npm)} i --package-lock-only --prefix ${handleSpacesInPath(manifestDir)}`, err => { if (err) { throw new Error('failed to create npmOutput list') } @@ -109,7 +109,7 @@ function provideComponent(data, opts = {}, path = '') { * @return {string} returns a string containing the result output. */ function getNpmListing(npm, allFilter, manifestDir) { - return `${npm} ls${allFilter} --omit=dev --package-lock-only --json --prefix ${manifestDir}`; + return `${handleSpacesInPath(npm)} ls${allFilter} --omit=dev --package-lock-only --json --prefix ${manifestDir}`; } diff --git a/src/providers/python_controller.js b/src/providers/python_controller.js index ac5a1a1..2981a25 100644 --- a/src/providers/python_controller.js +++ b/src/providers/python_controller.js @@ -6,7 +6,7 @@ import {environmentVariableIsPopulated,getCustom, handleSpacesInPath} from "../t function getPipFreezeOutput() { - return environmentVariableIsPopulated("EXHORT_PIP_FREEZE") ? new Buffer(process.env["EXHORT_PIP_FREEZE"],'base64').toString('ascii') : execSync(`${this.pathToPipBin} freeze --all`, err => { + return environmentVariableIsPopulated("EXHORT_PIP_FREEZE") ? new Buffer(process.env["EXHORT_PIP_FREEZE"],'base64').toString('ascii') : execSync(`${handleSpacesInPath(this.pathToPipBin)} freeze --all`, err => { if (err) { throw new Error('fail invoking pip freeze to fetch all installed dependencies in environment --> ' + err.message) } @@ -15,7 +15,7 @@ function getPipFreezeOutput() { function getPipShowOutput(depNames) { - return environmentVariableIsPopulated("EXHORT_PIP_SHOW") ? new Buffer(process.env["EXHORT_PIP_SHOW"],'base64').toString('ascii') : execSync(`${this.pathToPipBin} show ${depNames}`, err => { + return environmentVariableIsPopulated("EXHORT_PIP_SHOW") ? new Buffer(process.env["EXHORT_PIP_SHOW"],'base64').toString('ascii') : execSync(`${handleSpacesInPath(this.pathToPipBin)} show ${depNames}`, err => { if (err) { throw new Error('fail invoking pip show to fetch all installed dependencies metadata --> ' + err.message) } @@ -55,7 +55,7 @@ export default class Python_controller { { if(!this.realEnvironment) { this.pythonEnvDir = path.join(path.sep,"tmp","exhort_env_js") - execSync(`${this.pathToPythonBin} -m venv ${this.pythonEnvDir} `, err => { + execSync(`${handleSpacesInPath(this.pathToPythonBin)} -m venv ${this.pythonEnvDir} `, err => { if (err) { throw new Error('failed creating virtual python environment - ' + err.message) } @@ -70,7 +70,7 @@ export default class Python_controller { this.pathToPythonBin = path.join(this.pythonEnvDir,"bin","python") } // upgrade pip version to latest - execSync(`${this.pathToPythonBin} -m pip install --upgrade pip `, err => { + execSync(`${handleSpacesInPath(this.pathToPythonBin)} -m pip install --upgrade pip `, err => { if (err) { throw new Error('failed upgrading pip version on virtual python environment - ' + err.message) } @@ -104,7 +104,7 @@ export default class Python_controller { let installBestEfforts = getCustom("EXHORT_PYTHON_INSTALL_BEST_EFFORTS","false",this.options); if(installBestEfforts === "false") { - execSync(`${this.pathToPipBin} install -r ${handleSpacesInPath(this.pathToRequirements)}`, err =>{ + execSync(`${handleSpacesInPath(this.pathToPipBin)} install -r ${handleSpacesInPath(this.pathToRequirements)}`, err =>{ if (err) { throw new Error('fail installing requirements.txt manifest in created virtual python environment --> ' + err.message) } @@ -138,7 +138,7 @@ export default class Python_controller { let requirementsRows = requirementsContent.toString().split(EOL); requirementsRows.filter((line) => !line.trim().startsWith("#")).filter((line) => line.trim() !== "").forEach( (dependency) => { let dependencyName = getDependencyName(dependency); - execSync(`${this.pathToPipBin} install ${dependencyName}`, err =>{ + execSync(`${handleSpacesInPath(this.pathToPipBin)} install ${dependencyName}`, err =>{ if (err) { throw new Error(`Best efforts process - failed installing ${dependencyName} in created virtual python environment --> error message: ` + err.message) } @@ -152,7 +152,7 @@ export default class Python_controller { { if(!this.realEnvironment) { - execSync(`${this.pathToPipBin} uninstall -y -r ${handleSpacesInPath(this.pathToRequirements)}`, err =>{ + execSync(`${handleSpacesInPath(this.pathToPipBin)} uninstall -y -r ${handleSpacesInPath(this.pathToRequirements)}`, err =>{ if (err) { throw new Error('fail uninstalling requirements.txt in created virtual python environment --> ' + err.message) } @@ -400,7 +400,7 @@ function bringAllDependencies(dependencies, dependencyName, cachedEnvironmentDep function getDependencyTreeJsonFromPipDepTree(pipPath,pythonPath) { let dependencyTree try { - execSync(`${pipPath} install pipdeptree`) + execSync(`${handleSpacesInPath(pipPath)} install pipdeptree`) } catch (e) { throw new Error(`Couldn't install pipdeptree utility, reason: ${e.getMessage}`) } @@ -410,7 +410,7 @@ function getDependencyTreeJsonFromPipDepTree(pipPath,pythonPath) { dependencyTree = execSync(`pipdeptree --json`).toString() } else { - dependencyTree = execSync(`pipdeptree --json --python ${pythonPath} `).toString() + dependencyTree = execSync(`pipdeptree --json --python ${handleSpacesInPath(pythonPath)} `).toString() } } catch (e) { throw new Error(`couldn't produce dependency tree using pipdeptree tool, stop analysis, message -> ${e.getMessage}`) diff --git a/src/providers/python_pip.js b/src/providers/python_pip.js index a5f8050..3e92889 100644 --- a/src/providers/python_pip.js +++ b/src/providers/python_pip.js @@ -1,7 +1,12 @@ import {execSync} from "node:child_process"; import fs from 'node:fs' -import {environmentVariableIsPopulated, getCustom,getCustomPath } from "../tools.js"; +import { + environmentVariableIsPopulated, + getCustom, + getCustomPath, + handleSpacesInPath +} from "../tools.js"; import os from 'node:os' import path from 'node:path' import Sbom from '../sbom.js' @@ -151,14 +156,14 @@ function getPythonPipBinaries(binaries,opts) { let python = getCustomPath("python3",opts) let pip = getCustomPath("pip3",opts) try { - execSync(`${python} --version`) - execSync(`${pip} --version`) + execSync(`${handleSpacesInPath(python)} --version`) + execSync(`${handleSpacesInPath(pip)} --version`) } catch (e) { python = getCustomPath("python",opts) pip = getCustomPath("pip",opts) try { - execSync(`${python} --version`) - execSync(`${pip} --version`) + execSync(`${handleSpacesInPath(python)} --version`) + execSync(`${handleSpacesInPath(pip)} --version`) } catch (e) { throw new Error(`Couldn't get python binaries from supplied environment variables ${e.getMessage}`) }