Skip to content

fix(build): harden build scripts against shell-injection (CodeQL)#181

Open
simongdavies wants to merge 1 commit into
mainfrom
simongdavies/harden-build-scripts-codeql
Open

fix(build): harden build scripts against shell-injection (CodeQL)#181
simongdavies wants to merge 1 commit into
mainfrom
simongdavies/harden-build-scripts-codeql

Conversation

@simongdavies
Copy link
Copy Markdown
Member

Summary

Fixes two CodeQL js/shell-command-injection-from-environment alerts by replacing string-form execSync calls with array-form execFileSync (no shell) in build tooling. Resolves CodeQL alerts #2 and #11.

Changes

  • scripts/build-modules.js:71 — the Prettier format step previously interpolated BUILTIN_DIR into a shell string (execSync(\prettier --write "${BUILTIN_DIR}/.js"`)). It now runs via execFileSync(process.execPath, [require.resolve("prettier/bin/prettier.cjs"), "--write", join(BUILTIN_DIR, ".js")]). No shell parses the path; Prettier expands the glob itself. cwd: ROOT` preserved.
  • scripts/bash-bundle/build.mjs:57 — the esbuild bundle step previously concatenated args into a npx esbuild ... shell string. It now runs via execFileSync(process.execPath, [require.resolve("esbuild/bin/esbuild"), ...]) with every flag/value as its own array element. aliasArgs is kept as an array (no longer space-joined). Same flags, aliases, --minify, --tree-shaking, and --outfile — behaviour is identical.

Both call sites now invoke the local CLI through process.execPath (Node) against the resolved package bin, so there is no shell and resolution works on Windows without relying on npx/PATH.

Scope

Build tooling only — no runtime/source changes. Only the two flagged call sites were touched (the bumped-dependency logic, turndown-stub, etc. are untouched).

Validation

  • npm install (prepare → build:modules) and a standalone npm run build:modules both succeed on Windows, exercising both changed scripts (Prettier step + esbuild bash bundle).
  • Generated artifacts (builtin-modules/*.js, ha-modules.d.ts, bash bundle + hashes) are unchanged byte-for-bytegit status shows only the two scripts modified.
  • TS suite: 2321 passing. The only failures are pre-existing No native binding found for win32-x64-msvc errors (the code-validator native addon requires a clang + x86_64-hyperlight-none cross-compile toolchain not available on this Windows host) — unrelated to this change.

Commit is signed (GPG) and signed-off.

Copilot AI review requested due to automatic review settings June 3, 2026 11:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens build tooling against shell command injection (addressing CodeQL js/shell-command-injection-from-environment) by removing string-form shell invocations and switching to execFileSync with argument arrays, invoking local CLIs via process.execPath + require.resolve(...).

Changes:

  • Replace the Prettier formatting step in the modules build script with execFileSync(process.execPath, [...]) to avoid shell parsing of interpolated paths/globs.
  • Replace the npx esbuild ... shell string in the bash bundle build script with execFileSync(process.execPath, [...args]), keeping aliases as an array rather than a space-joined string.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
scripts/build-modules.js Runs Prettier via execFileSync + resolved local Prettier CLI to remove shell interpretation of interpolated paths.
scripts/bash-bundle/build.mjs Runs esbuild via execFileSync + resolved local esbuild CLI with array args (no npx, no shell).

…ction alerts

Remove the string-form execSync invocations flagged by CodeQL
js/shell-command-injection-from-environment at two build-tooling call sites:

- 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: the esbuild bundle step now uses esbuild's JS
  API (await build({ ... })) instead of spawning a CLI through a shell. The CLI
  --alias flags are mapped to the alias option and every other flag to its
  matching build option (bundle, format, platform, target, mainFields, outfile,
  minify, treeShaking). This removes the child process entirely and is portable:
  esbuild's CLI bin is a native binary on Linux, so it cannot be launched via
  `node`.

Build tooling only; the generated bash.js bundle is byte-for-byte identical and
no other generated artifacts change. 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>
@simongdavies simongdavies force-pushed the simongdavies/harden-build-scripts-codeql branch from d65a1fe to abf2dca Compare June 3, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants