Skip to content

security: SanitizeCommand preserves newlines — multi-line LLM output executes as multiple shell commands #360

@zorak1103

Description

@zorak1103

Summary

SanitizeCommand explicitly preserves newlines (\n). Since the sanitized string is passed to bash -c, newlines are treated identically to semicolons by the shell — a prompt-injected multi-line LLM response executes multiple commands when the user confirms with Y.

Details

// pkg/shell/shell.go:78 — newlines explicitly preserved
if unicode.IsControl(r) && r != '\n' && r != '\t' {
    continue
}

// pkg/shell/shell.go:133-135 — passed directly to bash
executeCommand = "bash"
args = []string{"-c", command}   // newlines = command separators

The sanitizer's own comment limits its scope to "display manipulation attacks". Shell metacharacters (;, &&, ||, |, $(), backticks) and newlines pass through unchanged.

Example attack: A prompt-injected LLM response ls\nrm -rf ~ displays as two apparently harmless separate lines in the confirmation prompt, but bash -c executes both when the user types Y.

This is an inherent design risk of the shell-execution feature (LLM output → shell), but the current SanitizeCommand provides no defense against it.

Affected file

  • pkg/shell/shell.go:62-84, 86-98, 123-145

Proposed remediation

Three-layer defense-in-depth (pipes/redirects stay allowed — legitimate use cases like du -a | sort -rh | head -3 must continue to work):

Layer 1 — Newline reject (blocking)

Change SanitizeCommand signature to return an error for multi-line output:

var ErrMultilineCommand = errors.New("command contains newline — rejected as potential injection")

func SanitizeCommand(command string) (string, error) {
    // ... existing ANSI/bidi cleanup ...
    if strings.ContainsAny(sanitized, "\n\r") {
        return "", ErrMultilineCommand
    }
    return sanitized, nil
}

Layer 2 — Heuristic warning (non-blocking)

Before the Y/n prompt, check for destructive patterns and print a visible warning:

⚠ WARNING: command contains potentially destructive patterns: [rm -rf, $(…)]

Patterns to match: rm -rf, | sh/bash/zsh, curl … |, $(), backticks, wget … |, > /dev/sd*, mkfs, dd if=.

Layer 3 — Documentation

Add a note to README.md and the shell subcommand help text warning that prompt injection via chat history, personas, or piped content can coerce the LLM into generating destructive commands.

Related

References

  • Security audit 2026-04-19, finding SGP-01 (High)
  • Exploit PoC: audit/2026-04-19/exploits/sgp-01.go (static, not executed)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions