Skip to content

fix(eslint): detect inline require('child_process').<method>() in no-child-process-interpolated-command - #49069

Merged
pelikhan merged 2 commits into
mainfrom
copilot/no-child-process-interpolated-command-fix
Jul 30, 2026
Merged

fix(eslint): detect inline require('child_process').<method>() in no-child-process-interpolated-command#49069
pelikhan merged 2 commits into
mainfrom
copilot/no-child-process-interpolated-command-fix

Conversation

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

no-child-process-interpolated-command missed the inline-require call shape require('child_process').execSync(...) because resolveChildProcessMethod required callee.object to be an Identifier — but for inline requires it's a CallExpression, so the rule never fired.

Changes

  • no-child-process-interpolated-command.ts — in resolveChildProcessMethod, split the existing callee.object.type !== Identifier early-return into two checks: first test whether callee.object is a CallExpression matching isRequireChildProcess and resolve the method directly; otherwise fall through to the existing identifier-binding path.

  • no-child-process-interpolated-command.test.ts — new invalid cases for require('child_process').execSync(\...`), require('node:child_process').execSync("..." + x), and require('child_process').spawn(`...`, { shell: true })`; new valid cases confirming static-command inline requires produce no false positives.

// previously undetected — now flagged
require('child_process').execSync(`rm -rf ${userInput}`);
require('node:child_process').execSync("rm -rf " + userInput);
require('child_process').spawn(`git checkout ${branch}`, { shell: true });

// static command — no false positive
require('child_process').execSync("git status");

…ild-process-interpolated-command

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix detection of inline child_process calls in ESLint rule fix(eslint): detect inline require('child_process').<method>() in no-child-process-interpolated-command Jul 30, 2026
Copilot AI requested a review from pelikhan July 30, 2026 06:41
@pelikhan
pelikhan marked this pull request as ready for review July 30, 2026 06:42
Copilot AI review requested due to automatic review settings July 30, 2026 06:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Not ready to approve

Inline detection incorrectly reports calls where require is locally shadowed.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Fixes inline child_process require calls being missed by the ESLint rule.

Changes:

  • Resolves methods called directly from require("child_process").
  • Adds static and dynamic inline-require test cases.
File summaries
File Description
eslint-factory/src/rules/no-child-process-interpolated-command.ts Adds inline-require method resolution.
eslint-factory/src/rules/no-child-process-interpolated-command.test.ts Tests static, interpolated, concatenated, and shell-enabled calls.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +138 to +141
if (callee.object.type === AST_NODE_TYPES.CallExpression && isRequireChildProcess(callee.object)) {
const method = callee.property.name;
return method === "exec" || method === "execSync" || method === "spawn" || method === "spawnSync" || method === "execFile" || method === "execFileSync" ? method : null;
}
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

Copy link
Copy Markdown
Contributor

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@-

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 13 AIC · ⊞ 8.4K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 100/100. 0% implementation tests (threshold: 30%). All tests verify design invariants with appropriate ESLint RuleTester assertions and comprehensive edge-case coverage.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: fix(eslint): detect inline require('child_process').<method>()

The fix is correct and well-targeted. The root cause (the early-return guarding against non-Identifier objects in resolveChildProcessMethod) is addressed cleanly, and the new test cases cover the three meaningful new shapes (template literal, string concat, spawn with shell).

One non-blocking suggestion: the method-membership ternary is duplicated verbatim at lines 140 and 147, and the same 6 methods are already listed in the methods array on line 128. Extracting a shared Set<ChildProcessMethod> would consolidate the single source of truth and prevent future drift.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 23.5 AIC · ⌖ 4.86 AIC · ⊞ 5.3K


if (callee.object.type === AST_NODE_TYPES.CallExpression && isRequireChildProcess(callee.object)) {
const method = callee.property.name;
return method === "exec" || method === "execSync" || method === "spawn" || method === "spawnSync" || method === "execFile" || method === "execFileSync" ? method : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The method-validation ternary here is identical to the one on line 147. Both inline the same 6 methods that also appear in the methods array on line 128. Consider extracting a shared constant — e.g. const CHILD_PROCESS_METHOD_SET = new Set<ChildProcessMethod>(["exec", "execSync", "spawn", "spawnSync", "execFile", "execFileSync"]) — and replacing both ternaries with CHILD_PROCESS_METHOD_SET.has(method) ? (method as ChildProcessMethod) : null to keep the list in one place and avoid drift if methods are added later.

@copilot please address this.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: COMMENT

Small, well-tested fix for the inline-require detection gap, but it copy-pastes an existing weak binding check and duplicates a method-validation ternary.

💡 Themes
  • The new inline-require branch reuses isRequireChildProcess, which resolves require by name only (no scope check), extending an already-known false-positive risk (shadowed require) to a second call site.
  • The allowed-method-name check (exec/execSync/spawn/spawnSync/execFile/execFileSync) is now duplicated verbatim across two branches instead of factored into one list/helper, risking future drift.
  • Test coverage for the new cases (execSync, string concat, spawn with shell:true) is solid and directly validates the fix.

🔎 Code quality review by PR Code Quality Reviewer · auto · 17.9 AIC · ⌖ 4.68 AIC · ⊞ 7.8K
Comment /review to run again

if (callee.object.type !== AST_NODE_TYPES.Identifier || callee.property.type !== AST_NODE_TYPES.Identifier) return null;
if (callee.property.type !== AST_NODE_TYPES.Identifier) return null;

if (callee.object.type === AST_NODE_TYPES.CallExpression && isRequireChildProcess(callee.object)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reusing isRequireChildProcess here inherits its known weakness: it matches any require identifier by name with no scope/binding check, so a shadowed require (e.g. a function parameter named require) is misidentified as the real Node.js require.

💡 Details

This extends the same flawed name-matching helper already noted in an earlier review comment on line 141 (for the identifier-binding path) to a new call site — doubling the false-positive exposure. Example:

function run(require, input) {
  require("child_process").execSync(`echo ${input}`);
}

A proper fix would have isRequireChildProcess verify the require identifier actually resolves to the unbound global, not just match by name.


if (callee.object.type === AST_NODE_TYPES.CallExpression && isRequireChildProcess(callee.object)) {
const method = callee.property.name;
return method === "exec" || method === "execSync" || method === "spawn" || method === "spawnSync" || method === "execFile" || method === "execFileSync" ? method : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The method allow-list ternary is now duplicated verbatim on lines 140 and 147, so a future edit to the supported method set only touching one copy silently reintroduces a gap.

💡 Details

Both branches repeat:

return method === "exec" || method === "execSync" || method === "spawn" || method === "spawnSync" || method === "execFile" || method === "execFileSync" ? method : null;

A ChildProcessMethod[] list (already declared for the identifier-callee branch at line 128) or a shared Set<string>/helper checked against ChildProcessMethod would remove the duplication and guarantee both paths stay in sync, e.g.:

const CHILD_PROCESS_METHOD_NAMES = new Set<ChildProcessMethod>(["exec","execSync","spawn","spawnSync","execFile","execFileSync"]);
function asChildProcessMethod(name: string): ChildProcessMethod | null {
  return CHILD_PROCESS_METHOD_NAMES.has(name as ChildProcessMethod) ? (name as ChildProcessMethod) : null;
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 31.2 AIC · ⌖ 4.94 AIC · ⊞ 7K
Comment /matt to run again


if (callee.object.type === AST_NODE_TYPES.CallExpression && isRequireChildProcess(callee.object)) {
const method = callee.property.name;
return method === "exec" || method === "execSync" || method === "spawn" || method === "spawnSync" || method === "execFile" || method === "execFileSync" ? method : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] The method-name whitelist on this line is duplicated verbatim on line 147 — both paths use the same inline ternary. If a new method is added to one but not the other, the rule silently misfires.

💡 Suggested refactor

SHELL_CONDITIONAL_METHODS already uses a Set; follow the same pattern here:

const CHILD_PROCESS_METHODS = new Set<string>(["exec", "execSync", "spawn", "spawnSync", "execFile", "execFileSync"]);

// replace both ternaries with:
return CHILD_PROCESS_METHODS.has(method) ? (method as ChildProcessMethod) : null;

This keeps both code paths in sync automatically.

@copilot please address this.

},
{
code: `require("child_process").spawn(\`git checkout \${branch}\`, { shell: true });`,
errors: [{ messageId: "interpolatedCommand", data: { kind: "interpolated template literal", method: "spawn" } }],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] The new invalid test cases cover exec/execSync and spawn, but execFile, execFileSync, and spawnSync with inline require() are not exercised. Since the method list on line 140 now handles all six methods for the inline-require path, missing tests leave that coverage gap invisible.

💡 Suggested additional test cases
{
  code: `require("child_process").execFile(\`git \${branch}\`, { shell: true });`,
  errors: [{ messageId: "interpolatedCommand", data: { kind: "interpolated template literal", method: "execFile" } }],
},
{
  code: `require("child_process").spawnSync("cmd " + arg, ["--"], { shell: true });`,
  errors: [{ messageId: "interpolatedCommand", data: { kind: "dynamic string concatenation", method: "spawnSync" } }],
},

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

Triage Summary

  • Category: bug
  • Risk: low
  • Priority Score: 52/100 (impact 22 + urgency 12 + quality 18)
  • Recommended Action: auto_merge

Small, well-tested eslint rule fix for inline require(child_process) detection. Not draft, CI green (30/30 checks passing). Good candidate for auto-merge.

Generated by 🔧 PR Triage Agent · auto · 54.9 AIC · ⌖ 5.64 AIC · ⊞ 7.9K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@pelikhan
pelikhan merged commit 46b33f8 into main Jul 30, 2026
41 of 42 checks passed
@pelikhan
pelikhan deleted the copilot/no-child-process-interpolated-command-fix branch July 30, 2026 07:39
Copilot stopped work on behalf of pelikhan due to an error July 30, 2026 07:39
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.84.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

no-child-process-interpolated-command misses inline require('child_process').(method)() calls

3 participants