Skip to content

amp, goose: refuse to format resume commands for hostile session IDs - #57

Merged
ashtom merged 3 commits into
entireio:mainfrom
SparshM8:fix/resume-command-injection
Sep 9, 2026
Merged

ashtom merged 3 commits into
entireio:mainfrom
SparshM8:fix/resume-command-injection

Conversation

@SparshM8

Copy link
Copy Markdown
Contributor

Summary

The Amp and Goose adapters build their resume command by concatenating the session ID from a hook payload directly into the command string returned to the Entire CLI. The CLI prints that string for the user to run verbatim, so a hostile session ID — for example evil; rm -rf /, evil$(whoami), or an ID containing ANSI escape sequences — would execute arbitrary commands (or corrupt the terminal display) when the user runs the printed command. The session ID is attacker-controllable in at least the common case of crafting or tampering with hook payloads.

This change adds input validation to FormatResumeCommand in both adapters and refuses to emit a resume command for any session ID outside the character set that real Amp and Goose identifiers use. The validation mirrors the safe-rune allowlist already established by the kilo and qwen adapters, extending the same defensive contract to the two adapters that were still missing it.

Why this matters

The native-agent path in the Entire CLI already validates session IDs before constructing its resume command (isLaunchableResumeSessionID). External agents are the only remaining path where an unchecked, attacker-controlled identifier flows into a user-executable command string. The damage model is severe: a single crafted hook payload can turn the CLI's own resume prompt into an arbitrary-command execution surface, and because the printed command is assumed to be safe, the user has no reason to inspect it. Validating at the source — the adapter that formats the command — is the deepest and cheapest fix available.

Changes

agents/entire-agent-amp/internal/amp/agent.go

  • FormatResumeCommand now validates the session ID against a safe-rune allowlist (a-zA-Z0-9-_.:) and returns an empty command when the ID is invalid. The empty-ID fallback (--last) is unchanged.

agents/entire-agent-goose/internal/goose/agent.go

  • FormatResumeCommand now validates the session ID against the same allowlist (goose names are YYYYMMDD_N, which the allowlist fully covers) and returns an empty command for invalid IDs. The handler already serializes the empty string as an empty Command field, so the CLI receives nothing to print instead of a hostile string.

agents/entire-agent-amp/internal/amp/agent_test.go (new)

  • TestFormatResumeCommandEmpty, TestFormatResumeCommandValidID, and TestFormatResumeCommandRefusesInjectionPayloads covering thread IDs, YYYYMMDD_N names, and injection payloads such as command separators, command substitution, newlines, path traversal, and ANSI escapes.

agents/entire-agent-goose/internal/goose/agent_test.go (new)

  • TestFormatResumeCommandValidID and TestFormatResumeCommandRefusesInvalidPayloads with the same payload matrix, including the empty ID which Goose previously formatted unconditionally.

Testing

Test Coverage
TestFormatResumeCommandEmpty (amp) existing --last fallback unchanged
TestFormatResumeCommandValidID (amp) T-12345, 20260816_42, dotted and mixed-case IDs pass
TestFormatResumeCommandRefusesInjectionPayloads (amp) ;, $(), backticks, newlines, .., ~, |, &&, ANSI escapes, surrounding whitespace all refused
TestFormatResumeCommandValidID (goose) 20260611_1, 20260816_42 and mixed-character IDs pass
TestFormatResumeCommandRefusesInvalidPayloads (goose) empty ID plus the full injection payload matrix refused

The new tests are deterministic unit tests: they need neither the Amp, Goose, nor Entire binaries.

Compatibility

The change is behavior-preserving for every deployment using real Amp thread IDs or Goose YYYYMMDD_N session names, which is the only case FormatResumeCommand could have been called with meaningfully before. The only behavioral difference surfaces when a hostile or malformed ID reaches the formatter, which is precisely the case that must not be format-preserving. No protocol JSON surface, hook event format, transcript format, or kilo/kiro/omp/qwen behavior changes.

Checklist

  • Follows the validation convention already established by the kilo and qwen adapters (safe-rune allowlist)
  • Adds deterministic unit tests (no agent binaries or Entire CLI required)
  • Preserves existing lifecycle test behavior in both adapters
  • No changes to protocol handlers or the entire-agent-tests compliance suite required

FormatResumeCommand concatenates the session ID, which arrives from a
hook payload and is never validated, directly into the string the Entire
CLI prints for the user to run. A hostile ID such as 'evil; rm -rf /'
would execute arbitrary commands when the user runs the printed command.

Add a safe-character allowlist that mirrors the validation already used
by the kilo and qwen adapters and refuse to emit a resume command for
any ID outside that set. Normal session IDs (amp thread IDs, goose
YYYYMMDD_N names) are unaffected. Regression tests cover both valid
identifiers and a range of injection payloads.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The validation safely blocks shell and option injection while preserving documented valid behavior with focused test coverage.

Pull request overview

Adds shell-injection defenses to Amp and Goose resume-command formatting.

Changes:

  • Validates session IDs against a safe ASCII allowlist and rejects option-like IDs.
  • Preserves Amp’s empty-ID --last behavior.
  • Adds unit coverage for valid IDs and hostile payloads.
File summaries
File Description
agents/entire-agent-amp/internal/amp/agent.go Validates Amp resume session IDs.
agents/entire-agent-amp/internal/amp/agent_test.go Tests valid, empty, and hostile Amp IDs.
agents/entire-agent-goose/internal/goose/agent.go Validates Goose resume session IDs.
agents/entire-agent-goose/internal/goose/agent_test.go Tests valid and rejected Goose IDs.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@ashtom
ashtom merged commit 04970de into entireio:main Sep 9, 2026
36 checks passed
@ashtom

ashtom commented Sep 9, 2026

Copy link
Copy Markdown
Member

@SparshM8 Thanks for the contribution!

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants