Skip to content

Drop sh -c in RunShellExecutor, add command allowlist #1604

Description

@ricardozanini

Problem

RunShellExecutor passes the assembled command string to ProcessBuilder("sh", "-c", command). This means shell metacharacters (;, |, &&, $(...), backticks) in jq-resolved values are interpreted by the shell, making any command allowlist trivially bypassable.

For example, even if only echo is allowed:

command: ${ "echo " + .user.input }

An input of hello; rm -rf / becomes sh -c 'echo hello; rm -rf /' — the shell executes both commands.

Proposal

1. Replace sh -c with direct ProcessBuilder execution using an argv array.

The spec already separates command (the executable) and arguments (the parameters). Map them directly:

  • commandargv[0]
  • arguments entries → argv[1..n] (key-value pairs become "--key=value", key-only becomes "--key")
command: echo
arguments:
  --user: john

Becomes: ProcessBuilder("echo", "--user=john") — no shell involved.

Without a shell, metacharacters are literal strings, not operators. Injection is eliminated structurally, not by filtering.

2. Add a command allowlist to WorkflowApplication.

WorkflowApplication.builder()
    .allowedShellCommands(List.of("echo", "grep", "curl"))
    .build()
  • The resolved command (argv[0]) is compared against the allowlist as an exact string match.
  • Empty list (default) = no shell execution allowed.
  • Integrating frameworks pass their configuration through this API.

3. Pipes, subshells, and redirection are no longer available.

This is intentional. Users who need pipelines should wrap them in a dedicated script and allowlist that script name. This keeps the trust model clear: deployers control what executables are available, workflow authors control how to invoke them.

Scope

  • RunShellExecutor and RunShellExecutorBuilder in impl-core
  • WorkflowApplication (new allowlist API)
  • CommandPropertySetter in impl-container is a separate concern (tracked separately)

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions