google-gemini / google-gemini/gemini-cli

bug: prompt templates built via String.replace corrupt payloads containing $ patterns ($&, $', )

Open Beginner friendly
#29,044 2 comments 0 reactions 0 assignees View on GitHub
area/core effort/small kind/bug priority/p2 status/bot-triaged
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.