Interlace ESLint
ESLint Interlace
Secure CodingRules

detect-child-process

Detects instances of `child_process` & non-literal `exec()` calls that may allow command injection. This rule is part of [`eslint-plugin-secure-coding`](https:/

Keywords: command injection, CWE-78, security, ESLint rule, child_process, exec, spawn, OS command injection, shell injection, auto-fix, LLM-optimized, code security

Detects instances of child_process & non-literal exec() calls that may allow command injection. This rule is part of eslint-plugin-secure-coding and provides LLM-optimized error messages with fix suggestions.

🚨 Security rule | 💡 Provides LLM-optimized guidance | ⚠️ Set to error in recommended

Quick Summary

AspectDetails
CWE ReferenceCWE-78 (OS Command Injection)
SeverityCritical (security vulnerability)
Auto-Fix⚠️ Suggests fixes (manual application)
CategorySecurity
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForNode.js applications, deployment scripts, CI/CD tools

Vulnerability and Risk

Vulnerability: Unsafe execution of child processes occurs when user input is passed to functions like exec, spawn, or execFile without proper validation or sanitization.

Risk: An attacker can inject arbitrary shell commands (Command Injection), allowing them to execute malicious code on the server with the privileges of the Node.js process. This can lead to full system compromise, data exfiltration, or denial of service.

Rule Details

This rule detects dangerous use of Node.js child_process methods that can lead to command injection attacks when user input reaches command execution.

Error Message Format

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

🔒 CWE-78 OWASP:A05 CVSS:9.8 | OS Command Injection detected | CRITICAL [SOC2,PCI-DSS,ISO27001,NIST-CSF]
   Fix: Review and apply the recommended fix | https://owasp.org/Top10/A05_2021/

Message Components

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-78 OWASP:A05 CVSS:9.8
Issue DescriptionSpecific vulnerabilityOS Command Injection detected
Severity & ComplianceImpact assessmentCRITICAL [SOC2,PCI-DSS,ISO27001,NIST-CSF]
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

Configuration

OptionTypeDefaultDescription
allowLiteralStringsbooleanfalseAllow exec() with literal strings
allowLiteralSpawnbooleanfalseAllow spawn() with literal arguments
additionalMethodsstring[][]Additional child_process methods to check

Examples

❌ Incorrect

// Command injection - CRITICAL risk
const { exec } = require('child_process');
exec(`git clone ${userRepo}`, callback); // Attacker can inject: `repo}; rm -rf /;`

// Shell interpretation - HIGH risk
exec(`npm install ${packageName}`, callback); // Command chaining possible

// Synchronous execution - HIGH risk
execSync(`curl ${userUrl}`); // Blocking + injection risk

// Spawn with string command - MEDIUM risk
spawn('bash', ['-c', userCommand]); // Shell interpretation

// Dynamic spawn arguments - MEDIUM risk
spawn(userCommand, [userArg1, userArg2]); // Argument injection

✅ Correct

// Safe execFile usage
const { execFile } = require('child_process');
execFile('git', ['clone', validatedRepo], { shell: false }, callback);

// Safe spawn usage
const { spawn } = require('child_process');
const git = spawn('git', ['clone', validatedRepo], { shell: false });

// With execa library (recommended)
import execa from 'execa';
await execa('git', ['clone', validatedRepo]);

// Input validation helper
function validateGitUrl(url: string): boolean {
  return /^https:\/\/github\.com\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+$/.test(url);
}

// Safe implementation
async function safeClone(repoUrl: string) {
  if (!validateGitUrl(repoUrl)) {
    throw new Error('Invalid repository URL');
  }

  return execa('git', ['clone', repoUrl], {
    shell: false,
    stripFinalNewline: true,
  });
}

Method Comparison

MethodSecurity RiskSafe UsageRecommendation
exec()HIGHOnly with literalsAvoid for user input
execSync()HIGHOnly with literalsAvoid for user input
execFile()LOW✅ Safe with arraysPreferred for security
spawn()MEDIUM✅ Safe with validationGood for complex commands
execaLOW✅ Best practicesRecommended library

Security Impact

Command Injection Attacks

# Attacker input: "repo}; rm -rf /; #"
# Resulting command: git clone repo}; rm -rf /; #
exec(`git clone ${userInput}`); // 💥 Server compromised

Prevention Strategy

  1. Use Arrays - Pass arguments as separate array elements
  2. Disable Shell - Set shell: false option
  3. Validate Input - Whitelist allowed characters/patterns
  4. Use Safe Libraries - Prefer execa over native child_process
  5. Sanitize Paths - Never allow ../ or absolute paths in commands

Common Patterns & Fixes

Git Operations

// ❌ DANGEROUS
exec(`git clone ${repoUrl}`);

// ✅ SAFE
execFile('git', ['clone', repoUrl], { shell: false });

Package Installation

// ❌ DANGEROUS
exec(`npm install ${packageName}`);

// ✅ SAFE
execFile('npm', ['install', packageName], { shell: false });

File Operations

// ❌ DANGEROUS
exec(`tar -xzf ${archivePath}`);

// ✅ SAFE
execFile('tar', ['-xzf', archivePath], { shell: false });

Dynamic Commands

// ❌ DANGEROUS
exec(`${userCommand} ${userArgs}`);

// ✅ SAFE
const ALLOWED_COMMANDS = ['ls', 'cat', 'head'];
if (ALLOWED_COMMANDS.includes(userCommand)) {
  execFile(userCommand, [userArgs], { shell: false });
}

Migration Guide

Phase 1: Audit

// Enable warnings first
{
  rules: {
    'secure-coding/detect-child-process': 'warn'
  }
}

Phase 2: Replace exec() calls

// Find all exec() usage
exec(command) → execFile(command, [], options)
exec(command + args) → execFile(command, [args], options)

Phase 3: Add validation

// Add input validation
function validateCommand(cmd: string): boolean {
  return /^[a-zA-Z0-9_-]+$/.test(cmd);
}

Phase 4: Use safer libraries

// Upgrade to execa
import execa from 'execa';
await execa('git', ['clone', repoUrl]);

Advanced Configuration

Allow Specific Patterns

{
  rules: {
    'secure-coding/detect-child-process': ['error', {
      allowLiteralStrings: true,  // Allow exec('literal command')
      additionalMethods: ['fork'], // Also check fork() calls
    }]
  }
}

Monorepo Setup

// Root package.json scripts are usually safe
{
  rules: {
    'secure-coding/detect-child-process': ['error', {
      allowLiteralStrings: true,
      ignorePaths: ['scripts/**', 'tools/**']
    }]
  }
}

Testing Security

// Test command injection attempts
const injectionAttempts = [
  'repo}; rm -rf /; #',
  'repo`rm -rf /`',
  'repo$(rm -rf /)',
  'repo; curl evil.com',
  'repo && evil-command',
];

for (const attempt of injectionAttempts) {
  expect(() => safeClone(attempt)).toThrow();
}

Comparison with Alternatives

Featuredetect-child-processeslint-plugin-securityeslint-plugin-node
Command Injection Detection✅ Yes⚠️ Limited⚠️ Limited
CWE Reference✅ CWE-78 included⚠️ Limited⚠️ Limited
LLM-Optimized✅ Yes❌ No❌ No
ESLint MCP✅ Optimized❌ No❌ No
Fix Suggestions✅ Detailed⚠️ Basic⚠️ Basic

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Command from Variable

Why: Command strings stored in variables are not traced.

// ❌ NOT DETECTED - Command from variable
const cmd = `git clone ${userInput}`;
exec(cmd);

Mitigation: Build commands only with validated, literal parts.

Aliased child_process

Why: Aliased imports not recognized.

// ❌ NOT DETECTED - Aliased import
const cp = require('child_process');
cp.exec(userCommand);

Mitigation: Use standard import names. Apply rule to wrappers.

Wrapper Functions

Why: Custom wrappers around exec not analyzed.

// ❌ NOT DETECTED - Wrapper function
runCommand(userInput); // Uses exec internally

Mitigation: Apply rule to wrapper implementations.

Environment Variable Injection

Why: env options with user data not checked.

// ❌ NOT DETECTED - Env injection
execFile('cmd', [], { env: { PATH: userInput } });

Mitigation: Validate all environment variable values.

Further Reading

On this page