Skip to content

Commit d65a1fe

Browse files
simongdaviesCopilot
andcommitted
fix(build): use execFileSync (no shell) in build scripts to resolve CodeQL shell-injection alerts
Replace string-form execSync invocations with array-form execFileSync (no shell) at two build-tooling call sites flagged by CodeQL js/shell-command-injection-from-environment: - scripts/build-modules.js: prettier --write now runs via execFileSync(process.execPath, [require.resolve("prettier/bin/prettier.cjs"), "--write", join(BUILTIN_DIR, "*.js")]). Prettier expands the glob itself, so no shell parses the interpolated path. - scripts/bash-bundle/build.mjs: esbuild now runs via execFileSync(process.execPath, [require.resolve("esbuild/bin/esbuild"), ...]) with each flag/value as its own array element (aliasArgs kept as an array instead of space-joined). Same flags, aliases, --minify, --tree-shaking, and outfile — behaviour is identical. Build tooling only; generated artifacts are unchanged byte-for-byte. Resolves CodeQL alerts #2 and #11. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
1 parent 1a7ea63 commit d65a1fe

2 files changed

Lines changed: 38 additions & 15 deletions

File tree

scripts/bash-bundle/build.mjs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
//
99
// Prerequisites: npm install (just-bash and esbuild must be in node_modules)
1010

11-
import { execSync } from "node:child_process";
11+
import { execFileSync } from "node:child_process";
1212
import { readFileSync, writeFileSync, existsSync, unlinkSync } from "node:fs";
1313
import { join, dirname } from "node:path";
1414
import { fileURLToPath } from "node:url";
15+
import { createRequire } from "node:module";
1516

1617
const __dirname = dirname(fileURLToPath(import.meta.url));
18+
const require = createRequire(import.meta.url);
1719
const repoRoot = join(__dirname, "..", "..");
1820
const outFile = join(repoRoot, "builtin-modules", "bash.js");
1921

@@ -50,17 +52,27 @@ const aliasArgs = [
5052
`--alias:node-liblzma=${join(stubDir, "liblzma-stub.mjs")}`,
5153
`--alias:@mongodb-js/zstd=${join(stubDir, "zstd-stub.mjs")}`,
5254
`--alias:sql.js=${join(stubDir, "sqljs-stub.mjs")}`,
53-
].join(" ");
55+
];
5456

5557
const tmpBundle = join(stubDir, "_tmp_bundle.js");
5658

57-
execSync(
58-
`npx esbuild ${entryFile} ` +
59-
`--bundle --format=esm --platform=neutral --target=es2020 ` +
60-
`--main-fields=module,main ` +
61-
`${aliasArgs} ` +
62-
`--outfile=${tmpBundle} ` +
63-
`--minify --tree-shaking=true`,
59+
// Invoke esbuild without a shell (execFileSync + args array) so no argument
60+
// can be reinterpreted by a shell. Each flag/value is its own array element.
61+
execFileSync(
62+
process.execPath,
63+
[
64+
require.resolve("esbuild/bin/esbuild"),
65+
entryFile,
66+
"--bundle",
67+
"--format=esm",
68+
"--platform=neutral",
69+
"--target=es2020",
70+
"--main-fields=module,main",
71+
...aliasArgs,
72+
`--outfile=${tmpBundle}`,
73+
"--minify",
74+
"--tree-shaking=true",
75+
],
6476
{ stdio: "inherit", cwd: repoRoot },
6577
);
6678

scripts/build-modules.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
* 7. Regenerates host-modules.d.ts
1313
*/
1414

15-
import { execSync } from "child_process";
15+
import { execSync, execFileSync } from "child_process";
1616
import { join, dirname } from "path";
1717
import { fileURLToPath } from "url";
18+
import { createRequire } from "module";
1819
import { existsSync, unlinkSync, readdirSync, statSync } from "fs";
1920

2021
const __dirname = dirname(fileURLToPath(import.meta.url));
22+
const require = createRequire(import.meta.url);
2123
const ROOT = join(__dirname, "..");
2224
const BUILTIN_DIR = join(ROOT, "builtin-modules");
2325
const PLUGINS_DIR = join(ROOT, "plugins");
@@ -68,10 +70,20 @@ execSync("npx tsx scripts/generate-ha-modules-dts.ts", {
6870
});
6971

7072
// Step 4: Format with Prettier
71-
execSync(`prettier --write "${BUILTIN_DIR}/*.js"`, {
72-
cwd: ROOT,
73-
stdio: "inherit",
74-
});
73+
// Invoke prettier without a shell (execFileSync) so the interpolated path
74+
// can't be interpreted as a shell command. Prettier expands the glob itself.
75+
execFileSync(
76+
process.execPath,
77+
[
78+
require.resolve("prettier/bin/prettier.cjs"),
79+
"--write",
80+
join(BUILTIN_DIR, "*.js"),
81+
],
82+
{
83+
cwd: ROOT,
84+
stdio: "inherit",
85+
},
86+
);
7587

7688
console.log("\nUpdating module hashes...");
7789

@@ -161,5 +173,4 @@ execSync("npx tsx scripts/generate-host-modules-dts.ts", {
161173
stdio: "inherit",
162174
});
163175

164-
165176
console.log("✓ Build complete");

0 commit comments

Comments
 (0)