Skip to content

Commit d5dab8f

Browse files
authored
Merge pull request #2 from Strum355/nsc/npm-execsync
chore: use invokeCommand instead of execSync
2 parents 7d6a91c + b4030de commit d5dab8f

File tree

9 files changed

+522
-80
lines changed

9 files changed

+522
-80
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,6 @@
104104
},
105105
"eslintIgnore": [
106106
"index.js"
107-
]
107+
],
108+
"packageManager": "[email protected]+sha512.0e82714d1b5b43c74610193cb20734897c1d00de89d0e18420aebc5977fa13d780a9cb05734624e81ebd81cc876cd464794850641c48b9544326b5622ca29971"
108109
}

src/providers/base_java.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@ export default class Base_Java {
109109
/** this method invokes command string in a process in a synchronous way.
110110
* @param bin - the command to be invoked
111111
* @param args - the args to pass to the binary
112-
* @param callback - function to invoke if an error was thrown
113112
* @protected
114113
*/
115-
_invokeCommand(bin, args, callback, opts={}) { return invokeCommand(bin, args, callback, opts) }
114+
_invokeCommand(bin, args, opts={}) { return invokeCommand(bin, args, opts) }
116115
}

src/providers/base_javascript.js

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { execSync } from "node:child_process"
21
import fs from 'node:fs'
32
import os from "node:os";
4-
import { handleSpacesInPath } from "../tools.js";
3+
import { handleSpacesInPath, invokeCommand } from "../tools.js";
54
import path from 'node:path'
65
import Sbom from '../sbom.js'
76
import { PackageURL } from 'packageurl-js'
@@ -29,10 +28,16 @@ export default class Base_javascript {
2928
throw new TypeError("_cmdName must be implemented");
3029
}
3130

31+
/**
32+
* @returns {Array<string>}
33+
*/
3234
_listCmdArgs() {
3335
throw new TypeError("_listCmdArgs must be implemented");
3436
}
3537

38+
/**
39+
* @returns {Array<string>}
40+
*/
3641
_updateLockFileCmdArgs() {
3742
throw new TypeError("_updateLockFileCmdArgs must be implemented");
3843
}
@@ -97,8 +102,7 @@ export default class Base_javascript {
97102
if (parts.length === 2) {
98103
purlNs = parts[0];
99104
purlName = parts[1];
100-
}
101-
else {
105+
} else {
102106
purlName = parts[0];
103107
}
104108
return new PackageURL('npm', purlNs, purlName, version, undefined, undefined);
@@ -154,8 +158,7 @@ export default class Base_javascript {
154158
Object.entries(dependencies)
155159
.filter(entry => entry[1].version !== undefined)
156160
.forEach(entry => {
157-
let name, artifact;
158-
[name, artifact] = entry;
161+
let [name, artifact] = entry;
159162
let purl = this.#toPurl(name, artifact.version);
160163
sbom.addDependency(from, purl)
161164
let transitiveDeps = artifact.dependencies
@@ -168,22 +171,20 @@ export default class Base_javascript {
168171
#executeListCmd(includeTransitive, manifestDir) {
169172
const listArgs = this._listCmdArgs(includeTransitive, manifestDir);
170173
try {
171-
return execSync(listArgs, {
172-
encoding: 'utf-8'
173-
});
174-
} catch (err) {
175-
throw new Error(`failed to execute command ${listArgs} - Error: ${err}`);
174+
invokeCommand(this._cmdName(), listArgs)
175+
} catch (error) {
176+
throw new Error(`failed to list dependencies via "${this._cmdName()} ${listArgs.join(' ')}" - Error: ${error}`, {cause: error});
176177
}
177178
}
178179

179180
#version() {
180181
try {
181-
execSync(`${handleSpacesInPath(this._cmdName())} --version`, {
182-
stdio: 'ignore',
183-
encoding: "utf8",
184-
});
185-
} catch (err) {
186-
throw new Error(`${this._cmdName()} is not accessible: ${err}`);
182+
invokeCommand(this._cmdName(), ['--version'], {stdio: 'ignore'});
183+
} catch (error) {
184+
if (error.code === 'ENOENT') {
185+
throw new Error(`${this._cmdName()} is not accessible`);
186+
}
187+
throw new Error(`failed to check for package manager binary at ${this._cmdName()}`, {cause: error})
187188
}
188189
}
189190

@@ -194,21 +195,17 @@ export default class Base_javascript {
194195
if (os.platform() === 'win32') {
195196
process.chdir(manifestDir)
196197
}
197-
const args = this._updateLockFileCmdArgs(manifestDir);
198198

199199
try {
200-
return execSync(args, {
201-
encoding: 'utf-8'
202-
});
203-
} catch (err) {
204-
throw new Error(`failed to execute command ${args} - Error: ${err}`);
200+
const args = this._updateLockFileCmdArgs(manifestDir);
201+
invokeCommand(this._cmdName(), args)
202+
} catch (error) {
203+
throw new Error(`failed to create lockfile "${args}" - Error: ${error}`, {cause: error});
205204
} finally {
206205
if (os.platform() === 'win32') {
207206
process.chdir(originalDir)
208207
}
209208
}
210-
211-
212209
}
213210
}
214211

src/providers/java_gradle.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,12 @@ export default class Java_gradle extends Base_java {
193193
*/
194194
#getProperties(manifestPath, opts) {
195195
let gradle = getCustomPath("gradle", opts);
196-
let properties
197-
properties = this._invokeCommand(gradle, ['properties'], error => {
196+
try {
197+
let properties = this._invokeCommand(gradle, ['properties'], {cwd: path.dirname(manifestPath)})
198+
return properties.toString()
199+
} catch (error) {
198200
throw new Error(`Couldn't get properties of ${this._getManifestName()} file , Error message returned from gradle binary => ${EOL} ${error.message}`)
199-
}, {
200-
cwd: path.dirname(manifestPath)
201-
})
202-
return properties.toString()
201+
}
203202
}
204203

205204
/**
@@ -237,12 +236,12 @@ export default class Java_gradle extends Base_java {
237236

238237
#getDependencies(manifest) {
239238
const gradle = getCustomPath("gradle")
240-
const commandResult = this._invokeCommand(gradle, ['dependencies'], error => {
239+
try {
240+
const commandResult = this._invokeCommand(gradle, ['dependencies'], {cwd: path.dirname(manifest)})
241+
return commandResult.toString()
242+
} catch (error) {
241243
throw new Error(`Couldn't run gradle dependencies command, error message returned from gradle binary => ${EOL} ${error.message}`)
242-
}, {
243-
cwd: path.dirname(manifest)
244-
})
245-
return commandResult.toString()
244+
}
246245
}
247246

248247
/**

src/providers/java_maven.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ export default class Java_maven extends Base_java {
7474
const mvn = this.#selectMvnRuntime(manifest, opts)
7575

7676
// clean maven target
77-
this._invokeCommand(mvn, ['-q', 'clean', '-f', manifest], error => {
77+
try {
78+
this._invokeCommand(mvn, ['-q', 'clean', '-f', manifest])
79+
} catch (error) {
7880
throw new Error(`failed to clean maven target`, {cause: error})
79-
})
81+
}
8082

8183
// create dependency graph in a temp file
8284
let tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'exhort_'))
@@ -93,9 +95,11 @@ export default class Java_maven extends Base_java {
9395
}
9496
})
9597
// execute dependency tree command
96-
this._invokeCommand(mvn, depTreeCmdArgs, error => {
98+
try {
99+
this._invokeCommand(mvn, depTreeCmdArgs)
100+
} catch (error) {
97101
throw new Error(`failed creating maven dependency tree`, {cause: error})
98-
})
102+
}
99103
// read dependency tree from temp file
100104
let content = fs.readFileSync(`${tmpDepTree}`)
101105
if (process.env["EXHORT_DEBUG"] === "true") {
@@ -138,9 +142,11 @@ export default class Java_maven extends Base_java {
138142
const targetPom = manifestPath
139143

140144
// create effective pom and save to temp file
141-
this._invokeCommand(mvn, ['-q', 'help:effective-pom', `-Doutput=${tmpEffectivePom}`, '-f', targetPom], error => {
145+
try {
146+
this._invokeCommand(mvn, ['-q', 'help:effective-pom', `-Doutput=${tmpEffectivePom}`, '-f', targetPom])
147+
} catch (error) {
142148
throw new Error(`failed creating maven effective pom`, {cause: error})
143-
})
149+
}
144150
// iterate over all dependencies in original pom and collect all ignored ones
145151
let ignored = this.#getDependencies(targetPom).filter(d => d.ignore)
146152
// iterate over all dependencies and create a package for every non-ignored one
@@ -205,24 +211,28 @@ export default class Java_maven extends Base_java {
205211
if (useMvnw) {
206212
const mvnw = this.#traverseForMvnw(manifestPath)
207213
if (mvnw !== undefined) {
208-
this._invokeCommand(mvnw, ['--version'], error => {
214+
try {
215+
this._invokeCommand(mvnw, ['--version'])
216+
} catch (error) {
209217
if (error.code === 'ENOENT') {
210218
useMvnw = false
211219
} else {
212220
throw new Error(`failed to check for mvnw`, {cause: error})
213221
}
214-
})
222+
}
215223
mvn = useMvnw ? mvnw : mvn
216224
}
217225
} else {
218226
// verify maven is accessible
219-
this._invokeCommand(mvn, ['--version'], error => {
227+
try {
228+
this._invokeCommand(mvn, ['--version'])
229+
} catch (error) {
220230
if (error.code === 'ENOENT') {
221231
throw new Error(`maven not accessible at "${mvn}"`)
222232
} else {
223233
throw new Error(`failed to check for maven`, {cause: error})
224234
}
225-
})
235+
}
226236
}
227237
return mvn
228238
}

src/providers/javascript_npm.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@ export default class Javascript_npm extends Base_javascript {
1111
}
1212

1313
_listCmdArgs(includeTransitive, manifestDir) {
14-
const depthArg = includeTransitive ? "--all" : "--depth=0";
15-
const manifestArg = manifestDir ? `--prefix ${manifestDir}` : "";
16-
17-
return `${this._cmdName()} ls ${depthArg} --package-lock-only --omit=dev --json ${manifestArg}`;
14+
const args = ['ls', includeTransitive ? '--all' : '--depth=0', '--package-lock-only', '--omit=dev', '--json']
15+
if (manifestDir) {
16+
args.push('--prefix', manifestDir)
17+
}
18+
return args
1819
}
1920

2021
_updateLockFileCmdArgs(manifestDir) {
21-
const manifestArg = manifestDir ? `--dir ${manifestDir}` : "";
22-
return `${this._cmdName()} install --package-lock-only ${manifestArg}`;
22+
const args = ['install', '--package-lock-only']
23+
if (manifestDir) {
24+
args.push('--dir', manifestDir)
25+
}
26+
return args;
2327
}
24-
2528
}

src/providers/javascript_pnpm.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,20 @@ export default class Javascript_pnpm extends Base_javascript {
1111
}
1212

1313
_listCmdArgs(includeTransitive, manifestDir) {
14-
const depthArg = includeTransitive ? "--depth=Infinity" : "--depth=0";
15-
const manifestArg = manifestDir ? `--dir ${manifestDir}` : "";
16-
return `${this._cmdName()} ls ${depthArg} ${manifestArg} --prod --json`;
14+
const args = ['ls', includeTransitive ? '--all' : '--depth=0', '--prod', '--json'];
15+
if (manifestDir) {
16+
args.push('--prefix', manifestDir);
17+
}
18+
return args;
1719
}
1820

1921
_updateLockFileCmdArgs(manifestDir) {
20-
const manifestArg = manifestDir ? `--dir ${manifestDir}` : "";
21-
return `${this._cmdName()} install --frozen-lockfile ${manifestArg}`;
22+
const args = ['install', '--frozen-lockfile'];
23+
if (manifestDir) {
24+
args.push('--prefix', manifestDir)
25+
}
26+
args.push(...[])
27+
return args;
2228
}
2329

2430
_buildDependencyTree(includeTransitive, manifest) {

src/tools.js

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -106,42 +106,31 @@ function hasSpaces(path) {
106106
return path.trim().includes(" ")
107107
}
108108

109-
110109
/**
111110
*
112111
* @param {string} cwd - directory for which to find the root of the git repository.
113112
*/
114113
export function getGitRootDir(cwd) {
115-
const root = invokeCommand('git', ['rev-parse', '--show-toplevel'], () => {}, {cwd: cwd})
116-
if (!root) {
114+
try {
115+
const root = invokeCommand('git', ['rev-parse', '--show-toplevel'], {cwd: cwd})
116+
return root.toString().trim()
117+
} catch (error) {
117118
return undefined
118119
}
119-
return root.toString().trim()
120120
}
121121

122122
/** this method invokes command string in a process in a synchronous way.
123123
* @param {string} bin - the command to be invoked
124124
* @param {Array<string>} args - the args to pass to the binary
125-
* @param callback - function to invoke if an error was thrown
126-
* @protected
127125
*/
128-
export function invokeCommand(bin, args, callback, opts={}) {
126+
export function invokeCommand(bin, args, opts={}) {
129127
// .bat and .cmd files can't be executed in windows with execFileSync, so we special case them
130128
// to use execSync here to keep the amount of escaping we need to do to a minimum.
131129
// https://nodejs.org/docs/latest-v20.x/api/child_process.html#spawning-bat-and-cmd-files-on-windows
132130
if (process.platform === 'win32' && (bin.endsWith(".bat") || bin.endsWith(".cmd"))) {
133-
try {
134-
args = args.map(arg => handleSpacesInPath(arg))
135-
return execSync(`${handleSpacesInPath(bin)} ${args.join(" ")}`, {...{stdio: 'pipe'}, ...opts})
136-
} catch(error) {
137-
callback(error)
138-
}
139-
return
131+
args = args.map(arg => handleSpacesInPath(arg))
132+
return execSync(`${handleSpacesInPath(bin)} ${args.join(" ")}`, {...{stdio: 'pipe', encoding: 'utf-8'}, ...opts})
140133
}
141134

142-
try {
143-
return execFileSync(bin, args, {...{stdio: 'pipe'}, ...opts})
144-
} catch(error) {
145-
callback(error)
146-
}
135+
return execFileSync(bin, args, {...{stdio: 'pipe', encoding: 'utf-8'}, ...opts})
147136
}

0 commit comments

Comments
 (0)