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)
Summary
SanitizeCommandexplicitly preserves newlines (\n). Since the sanitized string is passed tobash -c, newlines are treated identically to semicolons by the shell — a prompt-injected multi-line LLM response executes multiple commands when the user confirms withY.Details
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, butbash -cexecutes both when the user typesY.This is an inherent design risk of the shell-execution feature (LLM output → shell), but the current
SanitizeCommandprovides no defense against it.Affected file
pkg/shell/shell.go:62-84, 86-98, 123-145Proposed remediation
Three-layer defense-in-depth (pipes/redirects stay allowed — legitimate use cases like
du -a | sort -rh | head -3must continue to work):Layer 1 — Newline reject (blocking)
Change
SanitizeCommandsignature to return an error for multi-line output:Layer 2 — Heuristic warning (non-blocking)
Before the
Y/nprompt, check for destructive patterns and print a visible warning:Patterns to match:
rm -rf,| sh/bash/zsh,curl … |,$(), backticks,wget … |,> /dev/sd*,mkfs,dd if=.Layer 3 — Documentation
Add a note to
README.mdand theshellsubcommand help text warning that prompt injection via chat history, personas, or piped content can coerce the LLM into generating destructive commands.Related
References
audit/2026-04-19/exploits/sgp-01.go(static, not executed)