Skip to main content

Find command injection vulnerabilities

org.openrewrite.analysis.java.security.FindCommandInjection

Detects when user-controlled input flows into system command execution methods like Runtime.exec() or ProcessBuilder, which could allow attackers to execute arbitrary commands.

Recipe source

This recipe is only available to users of Moderne.

This recipe is available under the Moderne Proprietary License.

Examples

Example 1

ArrayAccessTaintTest#taintedArrayElement

Before
import javax.servlet.http.HttpServletRequest;

class Test {
void method(HttpServletRequest request) throws Exception {
String[] commands = new String[10];
commands[0] = request.getParameter("cmd");
Runtime.getRuntime().exec(commands[0]);
}
}
After
import javax.servlet.http.HttpServletRequest;

class Test {
void method(HttpServletRequest request) throws Exception {
String[] commands = new String[10];
commands[0] = request.getParameter("cmd");
/*~~(COMMAND_INJECTION use)~~>*/Runtime.getRuntime().exec(commands[0]);
}
}

Example 2

CommandInjectionWithStdlibTest#detectsRuntimeExecWithSystemProperty

Before
class CommandExecutor {
void execute() throws Exception {
String cmd = System.getProperty("user.command");
Runtime.getRuntime().exec(cmd);
}
}
After
class CommandExecutor {
void execute() throws Exception {
String cmd = System.getProperty("user.command");
/*~~(Command injection risk)~~>*/Runtime.getRuntime().exec(cmd);
}
}

Example 3

ContextSensitiveTaintAnalysisTest#contextSensitiveFieldPropagation

Unchanged
import javax.servlet.http.HttpServletRequest;

public class ContextExample {
private String command;

// Safe context - called with hardcoded value
public void initSafe() {
setCommand("ls -la");
}

// Unsafe context - called with user input
public void initUnsafe(HttpServletRequest request) {
String userCmd = request.getParameter("cmd");
setCommand(userCmd);
}

// Helper method that sets the field
private void setCommand(String cmd) {
this.command = cmd;
}

// Uses the field - should only be flagged if initUnsafe was called
public void execute() throws Exception {
Runtime.getRuntime().exec(this.command);
}
}

Example 4

FindCommandInjectionTest#detectsRuntimeExecWithUserInput

Before
import javax.servlet.http.HttpServletRequest;

class CommandExecutor {
void execute(HttpServletRequest request) throws Exception {
String cmd = request.getParameter("command");
Runtime.getRuntime().exec(cmd);
}
}
After
import javax.servlet.http.HttpServletRequest;

class CommandExecutor {
void execute(HttpServletRequest request) throws Exception {
String cmd = request.getParameter("command");
/*~~(COMMAND_INJECTION use)~~>*/Runtime.getRuntime().exec(cmd);
}
}

Example 5

MemberReferenceTaintTest#taintThroughInstanceMethodReference

Before
import javax.servlet.http.HttpServletRequest;
import java.util.function.Consumer;

class CommandProcessor {
void process(HttpServletRequest request) throws Exception {
String cmd = request.getParameter("command");
Consumer<String> executor = Runtime.getRuntime()::exec;
executor.accept(cmd);
}
}
After
import javax.servlet.http.HttpServletRequest;
import java.util.function.Consumer;

class CommandProcessor {
void process(HttpServletRequest request) throws Exception {
String cmd = request.getParameter("command");
Consumer<String> executor = Runtime.getRuntime()::exec;
/*~~>*/executor.accept(cmd);
}
}

Example 6

MethodReferenceFlowTest#taintFlowThroughInstanceMethodReference

Before
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;

class Test {
void method(HttpServletRequest request) {
List<String> commands = Arrays.asList(
request.getParameter("cmd1"),
request.getParameter("cmd2")
);
commands.forEach(this::executeCommand);
}

void executeCommand(String cmd) {
try {
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
// ignore
}
}
}
After
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;

class Test {
void method(HttpServletRequest request) {
List<String> commands = Arrays.asList(
request.getParameter("cmd1"),
request.getParameter("cmd2")
);
commands.forEach(this::executeCommand);
}

void executeCommand(String cmd) {
try {
/*~~(COMMAND_INJECTION use)~~>*/Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
// ignore
}
}
}

Example 7

NestedMethodCallTest#stepByStep

Before
import javax.servlet.http.HttpServletRequest;

public class StepByStep {
public void handleRequest(HttpServletRequest request) throws Exception {
// Step 1: Get tainted value
String tainted = request.getParameter("cmd");

// Step 2: Pass through transform
String transformed = transform(tainted);

// Step 3: Use in sink
Runtime.getRuntime().exec(transformed);
}

private String transform(String input) {
return "cmd " + input;
}
}
After
import javax.servlet.http.HttpServletRequest;

public class StepByStep {
public void handleRequest(HttpServletRequest request) throws Exception {
// Step 1: Get tainted value
String tainted = request.getParameter("cmd");

// Step 2: Pass through transform
String transformed = transform(tainted);

// Step 3: Use in sink
/*~~(COMMAND_INJECTION use)~~>*/Runtime.getRuntime().exec(transformed);
}

private String transform(String input) {
return "cmd " + input;
}
}

Example 8

ReturnValuePropagationTest#chainedMethodCalls

Before
import javax.servlet.http.HttpServletRequest;

public class ChainedCalls {
public void handleRequest(HttpServletRequest request) throws Exception {
// Taint should flow through the chain
String cmd = request.getParameter("cmd")
.trim()
.toUpperCase()
.replace("BAD", "GOOD");
Runtime.getRuntime().exec(cmd);
}
}
After
import javax.servlet.http.HttpServletRequest;

public class ChainedCalls {
public void handleRequest(HttpServletRequest request) throws Exception {
// Taint should flow through the chain
String cmd = request.getParameter("cmd")
.trim()
.toUpperCase()
.replace("BAD", "GOOD");
/*~~>*/Runtime.getRuntime().exec(cmd);
}
}

Usage

This recipe has no required configuration options. Users of Moderne can run it via the Moderne CLI:

You will need to have configured the Moderne CLI on your machine before you can run the following command.

shell
mod run . --recipe FindCommandInjection

If the recipe is not available locally, then you can install it using:

mod config recipes jar install io.moderne.recipe:rewrite-program-analysis:0.9.1

See how this recipe works across multiple open-source repositories

Run this recipe on OSS repos at scale with the Moderne SaaS.

The community edition of the Moderne platform enables you to easily run recipes across thousands of open-source repositories.

Please contact Moderne for more information about safely running the recipes on your own codebase in a private SaaS.

Data Tables

Taint flow

org.openrewrite.analysis.java.taint.table.TaintFlowTable

Records taint flows from sources to sinks with their taint types.

Column NameDescription
Source fileThe source file that the method call occurred in.
Source lineThe line number where the taint source is located.
SourceThe source code where taint originates.
Sink lineThe line number where the taint sink is located.
SinkThe sink code where taint flows to.
Taint typeThe taint type that matched at the sink.