Skip to content

[eslint-refiner] New rule: flag interpolated command strings passed to child_process exec/execSync/spawn (shell-injection covera [Content truncated due to length] #47494

Description

@github-actions

Summary

The existing no-exec-interpolated-command rule only covers the @actions/exec global (exec.exec(...) / exec.getExecOutput(...)). Its concern is argument space-splitting, not shell injection. Node's child_process family — execSync, exec, spawn/spawnSync with { shell: true } — runs the command string through a shell, so an interpolated command string is a genuine shell-injection vector. No rule in eslint-factory currently covers this stricter, more severe class.

Evidence (grounded in the repo)

  • actions/setup/js/push_repo_memory.test.cjs:1155-1165 hand-rolls a regex assertion to forbid exactly execSync(\git ${variable}`)` patterns. This proves in-repo demand and confirms ESLint does not cover it today — the guard had to be re-implemented as an ad-hoc unit test.
  • Real interpolated execSync calls in production code: actions/setup/js/start_mcp_gateway.cjs:693, :824, :896 (command strings built with ${...} and passed to execSync).

Proposal

Add a dedicated rule no-child-process-interpolated-command (semantically distinct from no-exec-interpolated-command, which stays focused on the @actions/exec arg-splitting concern).

Acceptance criteria

  • Flags an interpolated template literal or dynamic + concatenation passed as the command string to execSync, exec, spawn, spawnSync (and execFile/execFileSync when an options object with shell: true is present).
  • Resolves the callee to a child_process binding: require("child_process") / require("node:child_process"), ESM import, destructured { execSync }, and namespace member (cp.execSync). Reuse the existing binding-resolution helpers in src/rules/try-catch-rule-utils.ts (isRequireChildProcess, isChildProcessImportBinding, isChildProcessObjectBinding).
  • Does NOT flag fully static command strings (literal / no-expression template / static +).
  • Does NOT double-report the @actions/exec global — that remains owned by no-exec-interpolated-command.
  • Diagnostic names the shell-injection risk and recommends passing arguments via an array (execFileSync(cmd, [args])) or avoiding shell: true.
  • Unit tests cover: bound execSync, destructured { execSync }, namespace cp.execSync, static-safe command (valid), spawn(cmd, [args]) without shell (valid), and spawn(cmd, opts) / execSync with interpolation (flagged).
  • Registered in src/index.ts; documented in README.md with an "Out of scope" note distinguishing it from no-exec-interpolated-command.

Non-duplicate

Distinct from closed #47211 (execFileSync try/catch coverage) and #47212 (non-adjacent forward matching) — those concern error-handling, not command interpolation. no-exec-interpolated-command covers only the @actions/exec global.

Filed by ESLint Refiner (daily rule-quality refinement).

Generated by 🤖 ESLint Refiner · age00 317.3 AIC · ⌖ 13.1 AIC · ⊞ 4.6K ·

  • expires on Jul 29, 2026, 10:20 PM UTC-08:00

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!eslint

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions