Skip to content

Bash history expansion can silently drop agent-authored safe-output payloads #33366

Description

@corygehr

Summary

The bash wrapper that runs alongside engine: copilot (and other CLI-based engines) inherits the default interactive Bash behavior with histexpand enabled. Any double-quoted string the agent writes that contains !-bang patterns (e.g. "!**", "foo!bar") gets re-interpreted by Bash before reaching the command. When that re-interpretation fails (no matching history entry), bash exits before the command body runs — and any safe-output payload constructed inline is silently lost.

Reproduction

We hit this in production when an agent attempted to emit a safeoutputs payload whose body contained the literal characters !** (it was repeating back a prompt-injection marker as part of a user-facing explanation). The chain of events:

  1. Agent constructs a safeoutputs add_comment call with a multi-paragraph body containing !** somewhere mid-string.
  2. Bash sees the double-quoted argument, runs history expansion, fails to find a matching history event.
  3. Bash exits with status 1 and prints bash: !**: event not found to stderr.
  4. The safeoutputs MCP server never receives the call — the entire comment is dropped.
  5. The agent has no signal that anything went wrong; its tool-call log shows the invocation as initiated.
  6. The leaked stderr trips the threat detector, which adds a needs-review label and posts a CAUTION banner on the source issue — turning a silent dropped output into a public false-positive.

Minimal shell repro:

$ bash -ic 'echo "hello !** world"'
bash: !**: event not found
$ echo $?
1

With histexpand off, the same command runs fine:

$ bash -ic 'set +H; echo "hello !** world"'
hello !** world

Proposed fixes

Either of these would fully cover us:

A. Inject set +H (or shopt -u histexpand) at the top of the generated bash wrapper. Histexpand is meaningless for non-interactive shells anyway — turning it off should have zero side effects for legitimate command construction.

B. Use single-quoted heredocs (<<'EOF') when piping agent-authored payloads into commands like safeoutputs. That neutralizes the broader class of interpolation hazards (history expansion, parameter expansion, command substitution), not just bangs.

Both are reasonable; A is a one-line change. The relevant wrapper appears to be generated by pkg/workflow/mcp_cli_mount.go (lines ~164–180 in v0.72.1) where the safeoutputs:* shell allowlist is injected.

Why this matters

There isn't really a workflow-side mitigation. We can't prevent the agent from ever writing a ! inside a quoted string — even careful prompt instructions don't help reliably, because models love ! for emphasis or quotes-back of user content. A fix at the framework layer is the only durable answer.


Related: see the companion issue on body-allowed: false for close-discussion / close-issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions