Fix chat to use all active goals instead of only the first one#4778
Fix chat to use all active goals instead of only the first one#4778
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request aims to handle multiple active user goals in chat and goal-related functions. While it introduces valuable bug fixes and feature enhancements in get_goal_advice and _get_agentic_qa_prompt, it critically introduces Prompt Injection vulnerabilities by embedding unsanitized user data (goal titles and chat messages) directly into LLM prompts. This is particularly concerning in the main chat agent, which has access to sensitive tools. Furthermore, the extract_and_update_goal_progress function has a performance issue due to making an LLM call for each active goal. It is recommended to sanitize all user inputs to LLM prompts and refactor extract_and_update_goal_progress to use a single, more efficient LLM call.
| for goal in goals: | ||
| goal_title = goal.get('title', '') | ||
| current_value = goal.get('current_value', 0) | ||
| target_value = goal.get('target_value', 10) | ||
| goal_type = goal.get('goal_type', 'numeric') | ||
|
|
||
| prompt = f"""Analyze this message to see if it mentions progress toward this goal: | ||
| prompt = f"""Analyze this message to see if it mentions progress toward this goal: | ||
|
|
||
| Goal: "{goal_title}" | ||
| Goal Type: {goal_type} | ||
| Current Progress: {current_value} / {target_value} | ||
|
|
||
| User Message: "{text[:500]}" |
There was a problem hiding this comment.
The extract_and_update_goal_progress function embeds untrusted user messages and goal titles directly into a prompt, leading to a Prompt Injection vulnerability. An attacker can use this to manipulate the LLM's extraction logic, potentially leading to unauthorized updates of goal progress. This also poses a risk of indirect prompt injection. Additionally, the current implementation makes an LLM call for each active goal, which is inefficient and costly. It is crucial to sanitize user inputs (like text and goal_title) before embedding them into LLM prompts to prevent prompt breakout. Furthermore, consider refactoring this to make a single, more efficient LLM call that processes all active goals at once, rather than individual calls.
for goal in goals:
goal_title = goal.get('title', '').replace('"', '\"')
current_value = goal.get('current_value', 0)
target_value = goal.get('target_value', 10)
goal_type = goal.get('goal_type', 'numeric')
prompt = f"""Analyze this message to see if it mentions progress toward this goal:
Goal: "{goal_title}"
Goal Type: {goal_type}
Current Progress: {current_value} / {target_value}
User Message: "{text[:500].replace('"', '\"')}"""| for g in user_goals: | ||
| g_title = g.get('title', '') | ||
| g_current = g.get('current_value', 0) | ||
| g_target = g.get('target_value', 0) | ||
| goals_lines.append(f'- "{g_title}" (Progress: {g_current}/{g_target})') |
There was a problem hiding this comment.
The application constructs a system prompt by directly embedding user-supplied goal titles into an XML-like structure without proper sanitization. If a goal title contains malicious content (e.g., closing tags like </user_goals> followed by new instructions), it can break out of the intended context and manipulate the LLM's behavior. This is a Prompt Injection vulnerability that could allow an attacker to bypass security constraints or exfiltrate sensitive information using the LLM's available tools (such as Gmail or web search tools).
| for g in user_goals: | |
| g_title = g.get('title', '') | |
| g_current = g.get('current_value', 0) | |
| g_target = g.get('target_value', 0) | |
| goals_lines.append(f'- "{g_title}" (Progress: {g_current}/{g_target})') | |
| for g in user_goals: | |
| g_title = g.get('title', '').replace('<', '<').replace('>', '>').replace('"', '"') | |
| g_current = g.get('current_value', 0) | |
| g_target = g.get('target_value', 0) | |
| goals_lines.append(f'- "{g_title}" (Progress: {g_current}/{g_target})') |
Summary
get_goal_adviceto properly look up goals by ID instead of only working for the first active goalTest plan
🤖 Generated with Claude Code