Command injection — Process.run / Process.start invoked with a shell or interpolated command

ID

dart.code.command_injection

Severity

critical

Remediation Complexity

hard

Remediation Risk

high

Remediation Effort

medium

Resource

Code

Language

Dart

Description

Process.run / Process.start / Process.runSync is invoked with a shell (sh -c, cmd /c, PowerShell, runInShell: true) or with a string-interpolated executable. Tainted argument values are parsed by the shell and execute arbitrary commands.

Rationale

When a process is launched through a shell, the shell parses the command line and applies all its own metacharacter rules: ;, &&, ||, backticks, $(…​), redirection (>, <), globbing (*, ?), and quoting. ANY of these characters in a user-controlled value escapes the intended command and lets the attacker run their own.

Concrete attacks on Flutter (mostly desktop / server-side Dart):

  • Process.run('sh', ['-c', 'process $userId']) — attacker submits userId = "alice; cat /etc/passwd".

  • Process.run('cmd', ['/c', 'type $logfile']) on Windows — attacker controls logfile.

  • Process.run('powershell', ['-Command', '$script']) — total PowerShell injection.

  • runInShell: true plus an interpolated argument — every metacharacter in the argument is interpreted by the shell.

Even when a Flutter app does not run external commands today, introducing one with shell parsing creates a new attack surface reachable from any tainted source the app already accepts (deeplinks, push notifications, network payloads).

The following code illustrates a vulnerable pattern detected by this rule:

class CommandInjection {
  Future<void> shDashC(String userCmd) async {
    // VULNERABLE: Command injection — Process.run / Process.start invoked with a shell or interpolated command
    await Process.run('sh', ['-c', 'echo "$userCmd"']);
    // VULNERABLE: Command injection — Process.run / Process.start invoked with a shell or interpolated command
    await Process.run('/bin/sh', ['-c', userCmd]);
    // VULNERABLE: Command injection — Process.run / Process.start invoked with a shell or interpolated command
    await Process.run('/bin/bash', ['-c', userCmd]);
    // VULNERABLE: Command injection — Process.run / Process.start invoked with a shell or interpolated command
    await Process.run('zsh', ['-c', userCmd]);
  }
}

Remediation

Call the binary directly with a list of separate arguments — no shell, no interpolation. If you absolutely must invoke a shell, pass the dynamic value as a SEPARATE argument so the shell does not parse it.

// Vulnerable
await Process.run('sh', ['-c', 'process "$userId"']);
await Process.run('ls', [userArg], runInShell: true);
await Process.run('$tool', [arg]);

// Fixed: direct binary call, separate args, no shell
await Process.run('/usr/bin/process', [userId], runInShell: false);

Configuration

This detector does not need any configuration.

References

© 2026 Xygeni. All rights reserved