Skip to content

security: Command injection risk via shell command construction #3079

@louisgv

Description

@louisgv

Severity

HIGH

Location

Multiple files:

  • packages/cli/src/shared/agent-setup.ts (lines 253-254, 368-372, and others)
  • packages/cli/src/digitalocean/digitalocean.ts (lines 1329, 1447)

Description

The codebase constructs shell commands by interpolating variables into strings, relying on shellQuote() for escaping. While shellQuote() provides some protection, there are several potential risks:

  1. Base64 + shellQuote pattern: Code uses base64 encoding + shellQuote together

    const tokenB64 = Buffer.from(githubToken).toString(base64);
    ghCmd = `export GITHUB_TOKEN=$(printf '%s' ${shellQuote(tokenB64)} | base64 -d) && ${ghCmd}`;
  2. Complex command construction: Multiple layers of string interpolation make it hard to audit

  3. Dependency on shellQuote correctness: If shellQuote() has any edge cases or bugs, command injection could occur

Examples

// agent-setup.ts:253-254
const tokenB64 = Buffer.from(githubToken).toString(base64);
ghCmd = `export GITHUB_TOKEN=$(printf '%s' ${shellQuote(tokenB64)} | base64 -d) && ${ghCmd}`;

// agent-setup.ts:368-372
"openclaw onboard --non-interactive" +
  ` --openrouter-api-key ${shellQuote(apiKey)}` +
  " --gateway-auth token" +
  ` --gateway-token ${shellQuote(gatewayToken)}` +
  " --skip-health" +
  " --accept-risk";

// digitalocean.ts:1447
const fullCmd = `export TERM='${term}' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;

Risk

If shellQuote() fails to properly escape certain character sequences, or if API keys/tokens contain unexpected characters, command injection could occur during:

  • Agent installation
  • Configuration setup
  • Interactive sessions

Impact

Remote code execution on provisioned VMs during agent setup.

Recommendation

  1. Audit shellQuote() implementation for edge cases
  2. Consider using parameterized command execution (stdin/heredoc) instead of string interpolation where possible
  3. Add input validation on tokens/API keys before using in shell commands
  4. For critical operations, use SSH's built-in command escaping rather than manual quoting
  5. Add integration tests with adversarial inputs (special chars, newlines, etc.)

Example Safe Pattern

Instead of:

runner.runServer(`echo ${shellQuote(userInput)} > file`)

Use:

// Upload via stdin or temp file
const tmpFile = '/tmp/safe_upload';
await runner.uploadFile(localFile, tmpFile);
await runner.runServer(`cat < ${tmpFile} > target_file && rm ${tmpFile}`);

Metadata

Metadata

Assignees

No one assigned

    Labels

    in-progressIssue is being actively worked onsafe-to-workSecurity triage: safe for automated processingsecuritySecurity vulnerabilities and concerns

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions