Skip to content

[eslint-refiner] no-exec-interpolated-command: resolve single-level command-variable indirection (false negative) #47495

Description

@github-actions

Summary

no-exec-interpolated-command only inspects the syntactic shape of arguments[0]. Assigning the dynamic command to a variable first fully evades detection, so the space-splitting bug the rule targets slips through whenever the command is built one line earlier.

Evidence

  • The rule's own test documents this as intentionally out of scope: eslint-factory/src/rules/no-exec-interpolated-command.test.ts:22-23exec.exec(myCommand, [arg1]); is listed under valid with the comment "Command variable (identifier) — not a string literal, out of scope".
  • Real code builds command strings in variables before executing, e.g. actions/setup/js/create_pull_request.test.cjs:3064const cmdStr = \${cmd} ${(args || []).join(" ")}`;. A one-line refactor moving any of the many current exec.exec(`git ... ${x}`, [])` sites into a variable would silently disable the check.

Proposal

When arguments[0] is an Identifier, resolve a same-scope, write-once binding to its initializer and apply the existing getDynamicCommandKind check to that initializer.

Acceptance criteria

  • Flags const cmd = \git checkout ${b}`; exec.exec(cmd, []);and the dynamic+`-concatenation equivalent.
  • Only resolves bindings that are (a) in scope, (b) have exactly one definition with an initializer, and (c) are never reassigned. Params, imported bindings, multiply-assigned vars, and cross-function bindings are skipped to avoid false positives.
  • All current valid cases stay valid — in particular a variable holding a static string (const cmd = "git"; exec.exec(cmd, [b]);) is not flagged.
  • Report anchors on the call's first argument; message/messageId unchanged.
  • Tests: variable-holds-interpolation (flagged), variable-holds-static (valid), reassigned variable (valid/skipped), parameter-as-command (valid/skipped), and the +-concatenation-in-variable case (flagged).

Non-duplicate

Not covered by any open or closed eslint issue; the direct-argument case already works — this closes the documented indirection gap.

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