Back to Blog
high SEVERITY4 min read

runStreaming() Command Injection: Defense-in-Depth for Electron Child

An Electron application's `runStreaming()` utility accepted a command string and argument array without validating either, creating a latent command injection vector. The fix adds strict type checking and a whitelist regex that rejects shell metacharacters, bounding the failure mode even if caller input becomes attacker-influenced.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 22, 2026•Reviewed September 22, 2026

Answer Summary

The `runStreaming()` function in an Electron main process accepted a `cmd` string and `args` array without validation, enabling command injection if caller input were attacker-controlled. An attacker could execute arbitrary shell commands by injecting metacharacters through upstream data flows. The fix adds a whitelist regex `/^[A-Za-z0-9_.: \\/-]+$/` and explicit type checks for `cmd` and `args`, rejecting any call that doesn't pass. No CVE assigned; CWE-1357.

Vulnerability at a Glance

cweCWE-1357 (Reliance on Insufficiently Trustworthy Component)
fixAdded whitelist regex and type guards to reject dangerous command patterns
riskArbitrary code execution if upstream caller passes attacker-controlled input
languageJavaScript (Node.js/Electron)
root cause`runStreaming()` passed `cmd` directly to `child_process` without validation or sanitization
vulnerabilityCommand injection via unvalidated child_process invocation

Introduction

A helper function designed to stream subprocess output in an Electron application accepted arbitrary command strings without validation, creating a latent command injection vector. The runStreaming() utility wraps child_process to handle long-running Python package installations (specifically Paddle wheels and model archives), but its signature—runStreaming(cmd, args, opts)—trusted callers to provide safe inputs.

The problem: nothing in runStreaming() enforced that trust. A caller receiving user-influenced data could pass a malicious cmd like "; rm -rf / #" or an args array containing shell metacharacters. While the function used the array-form child_process API (avoiding shell: true), an attacker controlling both cmd and args could still exploit path traversal or argument injection depending on the target binary.

Affected Versions

Affected not applicable (first-party code)
Fixed in not applicable (first-party code) — see commit with SAFE_CMD regex addition
Ecosystem N/A
CVE / GHSA not assigned
CWE CWE-1357 (Reliance on Insufficiently Trustworthy Component)

The Vulnerability Explained

The vulnerable code accepted cmd as a string and args as an array without any inspection:

const runStreaming = (cmd, args, opts = {}) => new Promise((resolve, reject) => {
  if (quitting) { reject(new Error('应用正在退出')); return }
  let proc
  // ... spawns process with cmd and args directly

An attacker who influenced cmd could pass a relative path containing directory traversal (../../../malicious) or a binary name with shell metacharacters. More critically, if any caller concatenated user input into cmd before passing it, the lack of validation meant that injection propagated directly to process execution.

The real-world impact depends on data flows upstream of runStreaming(). The function handles package downloads for machine learning workflows—contexts where mirror URLs, proxy configurations, or user-supplied index URLs might reach this code path. An attacker compromising a package mirror or supplying a malicious index-url could potentially influence the command executed.

The Fix

The patch adds defense-in-depth validation that fails closed:

const SAFE_CMD = /^[A-Za-z0-9_.: \\/-]+$/
if (typeof cmd !== 'string' || !SAFE_CMD.test(cmd) || !Array.isArray(args) || args.some((a) => typeof a !== 'string')) {
  reject(new Error('非法的子进程调用参数'))
  return
}

This change introduces three specific protections:

  1. Type enforcement: cmd must be a string, args must be an array, and every element of args must be a string. This blocks prototype pollution attacks and type confusion bugs.

  2. Character whitelist: The SAFE_CMD regex permits only alphanumeric characters, safe path separators, and limited punctuation. It explicitly rejects semicolons (;), pipes (|), backticks (`), dollar signs ($), and other shell metacharacters.

  3. Explicit failure: Invalid inputs reject with a clear error message rather than proceeding with potentially dangerous data.

The fix maintains the existing array-form invocation (no shell), so the regex is strictly defense-in-depth—it closes off an entire class of vulnerabilities that would emerge if a caller ever passed unsanitized input.

Key Takeaways

  • Whitelist over blacklist: The SAFE_CMD regex defines permitted characters rather than trying to enumerate dangerous ones, avoiding the inevitable gaps in blacklist approaches.

  • Validate at trust boundaries: runStreaming() is a trust boundary between application logic and the operating system. Validation belongs here even if all current callers appear safe.

  • Array-form spawning isn't enough: While child_process.spawn(cmd, args) without shell: true prevents direct shell injection, a malicious cmd can still target unexpected binaries or exploit argument injection in the target program.

  • Type safety is security: The typeof checks catch JavaScript's dynamic typing footguns—args could be an object with a numeric length property, or cmd could be a String object with dangerous prototype methods.

  • Defense-in-depth justifies unexploitable fixes: Even without a demonstrated exploit, bounding failure modes prevents vulnerabilities from emerging as code evolves.

How Orbis AppSec Detected This

Source: The cmd parameter of runStreaming(), potentially influenced by upstream configuration values like mirror URLs or user settings.

Sink: child_process invocation within runStreaming(), where cmd and args are passed to process spawning.

Missing control: No validation of cmd contents, no type checking for args elements, and no restriction on shell metacharacters in command paths.

CWE: CWE-1357 (Reliance on Insufficiently Trustworthy Component) — the code relied on callers to provide trustworthy inputs without enforcing that contract.

Fix: Added whitelist regex and explicit type guards to reject any command string containing dangerous characters or any malformed argument array.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

The runStreaming() fix demonstrates how defense-in-depth validation transforms a latent vulnerability into an explicit, bounded failure mode. By whitelisting safe command characters and enforcing type contracts, the code now fails securely even against hypothetical attacker-controlled inputs—protecting against supply chain compromises and configuration injection attacks that might emerge in future code changes.

Prevention and further reading

Frequently Asked Questions

Does the `runStreaming()` fix change the function's return type or Promise behavior?

No. The fix adds early rejection with `new Error('非法的子进程调用参数')` for invalid inputs, but successful calls resolve identically. The Promise interface remains unchanged.

Why validate `args` as an array of strings when `child_process` already enforces this?

Defense-in-depth. The check ensures that even if a caller passes a malformed `args` value (e.g., through prototype pollution or type confusion), the function fails explicitly before reaching `child_process`.

Which characters does the `SAFE_CMD` regex explicitly allow in command paths?

Letters, digits, underscore, period, colon, space, forward slash, backslash, and hyphen. Notably, it excludes semicolons, pipes, backticks, dollar signs, and other shell metacharacters that enable command chaining.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #23

Related Articles

critical

webapp-testing Server Script: Shell Injection via Unquoted Command

A critical command injection vulnerability in a web server testing utility allowed attackers to execute arbitrary shell commands by injecting metacharacters into command-line arguments. The fix removes shell interpretation and safely tokenizes user-supplied commands using `shlex.split()`, while preserving support for legitimate directory-change workflows.

high

THiNX Device Platform: Command Injection via source.owner in Git Fetch

The THiNX device management platform fixed a critical vulnerability where the `source.owner` parameter flowed directly into shell commands during git repository operations. An attacker with source configuration access could inject arbitrary shell commands by providing malicious owner values containing metacharacters like `; curl`.

critical

backtest.js CLI args: Unvalidated --days and --start Options

The backtest.js command-line tool accepted --days and --start arguments without validation, creating a command injection vector when the script is invoked by upstream processes with untrusted input. The fix adds strict input validation: --days must be a positive integer ≤ 3650, and --start must match the YYYY-MM-DD date format.

critical

Tauri type_text() Command Injection via Control Characters in

The type_text() system command handler in Tauri accepted arbitrary text up to 2000 UTF-16 characters without validating content, allowing injection of control characters that SendInput's Unicode path interprets as real key presses. An attacker could inject newline, escape, or tab characters to trigger actions in the focused window beyond mere text typing.

critical

docker_rpc.uc Command Injection: Unsanitized RPC Parameters

A critical command injection vulnerability in the Docker RPC handler allowed authenticated attackers to execute arbitrary system commands by injecting shell metacharacters into container ID, port, user ID, or command parameters. The fix validates all user-supplied inputs against strict whitelist patterns before interpolating them into shell commands.

high

picomatch 2.3.1 ReDoS: Extglob Pattern Catastrophic Backtracking

picomatch versions below 2.3.2, 3.0.2, or 4.0.4 contain a Regular Expression Denial of Service vulnerability in extglob pattern parsing. An attacker can cause catastrophic backtracking with patterns containing nested alternations and quantifiers, freezing any Node.js process that evaluates untrusted glob expressions.