Skip to content

Commit 2e9c8d3

Browse files
committed
fix: improve subprocess spawning portability
1 parent 877d015 commit 2e9c8d3

5 files changed

Lines changed: 39 additions & 52 deletions

File tree

lib/analyzer/image-inspector.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,11 @@ async function getImageArchive(
212212

213213
try {
214214
inspectResult = await getInspectResult(docker, targetImage);
215-
} catch {
216-
debug(`${targetImage} does not exist locally, proceeding to pull image.`);
215+
} catch (error) {
216+
debug(
217+
`${targetImage} does not exist locally, proceeding to pull image.`,
218+
error.stack || error,
219+
);
217220
}
218221

219222
if (inspectResult === undefined) {

lib/sub-process.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as childProcess from "child_process";
2-
import { quoteAll } from "shescape/stateless";
32

43
export { execute, CmdOutput };
54
interface CmdOutput {
@@ -13,13 +12,15 @@ function execute(
1312
options?,
1413
): Promise<CmdOutput> {
1514
const spawnOptions: any = {
16-
shell: process.platform !== "win32" ? "/bin/bash" : true,
15+
// Some distributions may not have /bin/bash, which would cause `child_process.spawn` to fail.
16+
// By setting `shell: false`, we tell `spawn` to execute the command directly without a shell,
17+
// which is more portable.
18+
shell: false,
1719
env: { ...process.env },
1820
};
1921
if (options && options.cwd) {
2022
spawnOptions.cwd = options.cwd;
2123
}
22-
args = quoteAll(args, { ...spawnOptions, flagProtection: false });
2324

2425
// Before spawning an external process, we look if we need to restore the system proxy configuration,
2526
// which overrides the cli internal proxy configuration.

package-lock.json

Lines changed: 0 additions & 46 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
"mkdirp": "^1.0.4",
4646
"packageurl-js": "1.2.0",
4747
"semver": "^7.6.3",
48-
"shescape": "2.1.0",
4948
"snyk-nodejs-lockfile-parser": "^2.0.0",
5049
"snyk-poetry-lockfile-parser": "^1.4.0",
5150
"snyk-resolve-deps": "^4.7.1",

test/system/plugin.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { DepGraph } from "@snyk/dep-graph";
22
import * as plugin from "../../lib";
3+
import * as subProcess from "../../lib/sub-process";
34
import { getFixture } from "../util";
45

56
describe("plugin", () => {
@@ -91,6 +92,35 @@ describe("plugin", () => {
9192
});
9293
});
9394

95+
describe("when scanning a locally loaded image", () => {
96+
const imageName = "busybox";
97+
const imageTag = "latest";
98+
const imageNameWithTag = `${imageName}:${imageTag}`;
99+
100+
beforeAll(async () => {
101+
const fixturePath = getFixture([
102+
"../fixtures/docker-archives",
103+
"skopeo-copy/busybox.tar",
104+
]);
105+
await subProcess.execute("docker", ["load", "--input", fixturePath]);
106+
}, 10000); // 10s timeout for loading image
107+
108+
afterAll(async () => {
109+
await subProcess.execute("docker", ["rmi", imageNameWithTag]);
110+
});
111+
112+
test("should successfully scan a local image loaded from a tar archive", async () => {
113+
const pluginResult = await plugin.scan({ path: imageNameWithTag });
114+
const depGraph: DepGraph = pluginResult.scanResults[0].facts.find(
115+
(fact) => fact.type === "depGraph",
116+
)!.data;
117+
118+
expect(depGraph.rootPkg.name).toEqual(`docker-image|${imageName}`);
119+
expect(depGraph.rootPkg.version).toEqual(imageTag);
120+
expect(pluginResult.scanResults[0].identity.type).toEqual("linux");
121+
});
122+
});
123+
94124
test("image pulled by tag has version set", async () => {
95125
const imageNameAndTag = `nginx:1.19.0`;
96126

0 commit comments

Comments
 (0)