google-gemini / google-gemini/gemini-cli
bug: prompt templates built via String.replace corrupt payloads containing $ patterns ($&, $', )
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
## What happened?
Two LLM prompt templates are filled with `String.prototype.replace('{placeholder}', userData)` where `userData` is model/tool/user-controlled text. In JavaScript, `$` sequences in the *replacement* string have special meaning: `$&` inserts the matched substring (the placeholder token), `` $` `` and `$'` insert the portions before/after the match, `$$` collapses to `$`. Tool outputs and edit payloads routinely contain these (ANSI-C quoting `$'\n'`, jQuery/PHP `$$`, template literals `` $` `` …), silently corrupting the prompts sent to the summarizer and edit-fixer models.
## Affected code
`packages/core/src/utils/summarizer.ts:87-90`:
```ts
const prompt = SUMMARIZE_TOOL_OUTPUT_PROMPT.replace(
'{maxOutputTokens}',
String(maxOutputTokens),
).replace('{textToSummarize}', textToSummarize);
```
`packages/core/src/utils/llm-edit-fixer.ts:168-172`:
```ts
const userPrompt = EDIT_USER_PROMPT.replace('{instruction}', instruction)
.replace('{old_string}', old_string)
.replace('{new_string}', new_string)
.replace('{error}', error)
.replace('{current_content}', current_content);
```
## Example corruption
If `textToSummarize` contains the literal text `$'` (extremely common in shell scripts), everything between the placeholder position and the end of the prompt template is duplicated into the middle of the prompt; `$$` loses its dollar; `$&` injects the literal placeholder token into the payload.
## How can this be reproduced?
```ts
'Body: {textToSummarize}'.replace('{textToSummarize}', "echo $'newline'")
// -> 'Body: echo newline'' — template tail injected mid-payload
```
## What did you expect to happen?
Payloads are inserted literally.
## Suggested direction
Use a replacement-function form which treats the return value literally:
`tpl.replace('{textToSummarize}', () => textToSummarize)` — apply at all seven call sites (a shared helper avoids recurrence).
---
*Found by source audit on current `main` (commit `5411f113c`); platform-independent. No open issue/PR covering this was found (searched: replace prompt template escape).*
Contributor guide
Research direction
Start with the replacement chains in packages/core/src/utils/summarizer.ts and packages/core/src/utils/llm-edit-fixer.ts, then locate the remaining prompt-template call sites. Reproduce the issue with payloads containing $', $$, $` and $&, and verify that all seven insertions preserve the payload literally without corrupting the surrounding prompt.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- ai, cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100