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
| Aspect | Details |
|---|---|
| CWE Reference | CWE-78 (OS Command Injection) |
| Severity | Critical (security vulnerability) |
| Auto-Fix | ⚠️ Suggests fixes (manual application) |
| Category | Security |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | Node.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
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-78 OWASP:A05 CVSS:9.8 |
| Issue Description | Specific vulnerability | OS Command Injection detected |
| Severity & Compliance | Impact assessment | CRITICAL [SOC2,PCI-DSS,ISO27001,NIST-CSF] |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP Top 10 |
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
allowLiteralStrings | boolean | false | Allow exec() with literal strings |
allowLiteralSpawn | boolean | false | Allow spawn() with literal arguments |
additionalMethods | string[] | [] | 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
| Method | Security Risk | Safe Usage | Recommendation |
|---|---|---|---|
exec() | HIGH | Only with literals | Avoid for user input |
execSync() | HIGH | Only with literals | Avoid for user input |
execFile() | LOW | ✅ Safe with arrays | Preferred for security |
spawn() | MEDIUM | ✅ Safe with validation | Good for complex commands |
execa | LOW | ✅ Best practices | Recommended library |
Security Impact
Command Injection Attacks
# Attacker input: "repo}; rm -rf /; #"
# Resulting command: git clone repo}; rm -rf /; #
exec(`git clone ${userInput}`); // 💥 Server compromisedPrevention Strategy
- Use Arrays - Pass arguments as separate array elements
- Disable Shell - Set
shell: falseoption - Validate Input - Whitelist allowed characters/patterns
- Use Safe Libraries - Prefer
execaover native child_process - 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
| Feature | detect-child-process | eslint-plugin-security | eslint-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 |
Related Rules
detect-eval-with-expression- Prevents code injection via eval()detect-non-literal-fs-filename- Prevents path traversaldetect-object-injection- Prevents prototype pollutiondetect-non-literal-regexp- Prevents ReDoS attacks
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 internallyMitigation: 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
- OWASP Command Injection - Command injection attack guide
- Node.js Child Process Security - Node.js child_process security
- CWE-78: OS Command Injection - Official CWE entry
- Execa Library - Safer child process execution
- ESLint MCP Setup - Enable AI assistant integration