Fix shell heredoc injection via prompt/candidate content#1
Open
iFuzzle wants to merge 1 commit into
Open
Conversation
BuildCommand interpolated the prompt directly into a bash -c heredoc with a static delimiter (__NIGEL_PROMPT_EOF__). Since the prompt is built from candidate_source/$INPUT data that can originate from untrusted repository content, a candidate containing a line matching the delimiter would close the heredoc early and cause the remaining text to be parsed and executed as an arbitrary shell command. Both claude -p and codex exec --json - already support reading the prompt from stdin, so BuildCommand no longer embeds the prompt at all; RunAICommand now sets cmd.Stdin directly instead, so the prompt is never parsed by the shell regardless of its contents. (Generated by Claude)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ClaudeBackend.BuildCommandandCodexBackend.BuildCommandinterpolate the (possibly untrusted) prompt directly into abash -cstring using a heredoc with a static delimiter:The prompt is built via
$INPUTinterpolation from candidate data (InterpolatePromptinexecutor.go), and candidates come from whatever a task'scandidate_sourcecommand prints - e.g. repository-derived data like function/file names. If a candidate value contains a line that happens to equal__NIGEL_PROMPT_EOF__, the heredoc closes early and everything after it in the assembled string is parsed and executed as a normal shell command bybash -c, with the privileges of whoever is runningnigel.InterpolateCommandalready shell-quotes$CANDIDATEfor this exact reason - that protection just never extended to the AI-invocation heredoc.Fix
Stop embedding the prompt in the shell string entirely. Both
claude -pandcodex exec --json -already support reading the prompt from stdin (that's exactly why the heredoc was there in the first place, per the removed comment).BuildCommandnow only returns the command/flags, andRunAICommandsetscmd.Stdinto the prompt directly - so the prompt is never parsed by the shell, no matter what it contains.Testing
go build ./srcsucceedsgo test ./src/...- all existing tests pass with updatedBuildCommandsignature/expectationsTestRunAICommandPromptNeverParsedByShell: a regression test that feeds a prompt containing an embedded fake heredoc terminator plus atouchpayload throughRunAICommand, and asserts the payload is never executed and the prompt is delivered byte-for-byte via stdin insteadTestCodexBuildCommandExcludesPromptFromShellString(Generated by Claude)