Skip to main content

no-child-process-exec

Disallow child_process.exec() and child_process.execSync() shell-backed execution APIs.

Targeted pattern scopeโ€‹

This rule targets exec() and execSync() when they are imported from child_process or node:child_process, destructured from require(...), or called through a namespace binding created from those modules.

What this rule reportsโ€‹

This rule reports direct use of child_process.exec() and child_process.execSync() because both APIs execute a command string through a shell.

Why this rule existsโ€‹

Shell-backed command execution is harder to review safely than argv-separated process launches. When user-controlled data is concatenated into a command string, it can become command injection.

For SDL-oriented code review, spawn() and execFile() are generally easier to reason about because they keep the executable path and the arguments separate.

โŒ Incorrectโ€‹

import { exec } from "node:child_process";

exec(`git show ${userSuppliedRef}`);
const { execSync } = require("child_process");

execSync("tar -xf " + archivePath);
import * as childProcess from "node:child_process";

childProcess.exec("convert " + inputPath);

โœ… Correctโ€‹

import { execFile } from "node:child_process";

execFile("git", ["show", userSuppliedRef]);
const { spawn } = require("child_process");

spawn("tar", ["-xf", archivePath], { shell: false });

Behavior and migration notesโ€‹

This rule intentionally focuses on direct child_process bindings and does not attempt to reason about custom wrapper utilities that may call exec() internally.

ESLint flat config exampleโ€‹

import sdl from "eslint-plugin-sdl-2";

export default [
{
plugins: { sdl },
rules: {
"sdl/no-child-process-exec": "error",
},
},
];

When not to use itโ€‹

If your project intentionally permits shell-backed command execution and you already review all command construction paths carefully, this rule may be too strict.

Package documentationโ€‹

Further readingโ€‹

Rule catalog ID: R062