Sanitize Tags Unicode Block at prompt level (#4047)

This commit is contained in:
Amed Rodriguez
2025-08-15 09:12:05 -07:00
committed by GitHub
parent 85963689ce
commit 6e022a974a
3 changed files with 166 additions and 30 deletions
+111 -5
View File
@@ -6,7 +6,7 @@ use crate::agents::extension::ExtensionInfo;
use crate::agents::router_tool_selector::RouterToolSelectionStrategy;
use crate::agents::router_tools::{llm_search_tool_prompt, vector_search_tool_prompt};
use crate::providers::base::get_current_model;
use crate::{config::Config, prompt_template};
use crate::{config::Config, prompt_template, utils::sanitize_unicode_tags};
pub struct PromptManager {
system_prompt_override: Option<String>,
@@ -83,7 +83,18 @@ impl PromptManager {
));
}
context.insert("extensions", serde_json::to_value(extensions_info).unwrap());
let sanitized_extensions_info: Vec<ExtensionInfo> = extensions_info
.into_iter()
.map(|mut ext_info| {
ext_info.instructions = sanitize_unicode_tags(&ext_info.instructions);
ext_info
})
.collect();
context.insert(
"extensions",
serde_json::to_value(sanitized_extensions_info).unwrap(),
);
match tool_selection_strategy {
Some(RouterToolSelectionStrategy::Vector) => {
@@ -118,7 +129,8 @@ impl PromptManager {
// Conditionally load the override prompt or the global system prompt
let base_prompt = if let Some(override_prompt) = &self.system_prompt_override {
prompt_template::render_inline_once(override_prompt, &context)
let sanitized_override_prompt = sanitize_unicode_tags(override_prompt);
prompt_template::render_inline_once(&sanitized_override_prompt, &context)
.expect("Prompt should render")
} else if let Some(model) = &model_to_use {
// Use the fuzzy mapping to determine the prompt file, or fall back to legacy logic
@@ -149,13 +161,18 @@ impl PromptManager {
.push("Right now you are *NOT* in the chat only mode and have access to tool use and system.".to_string());
}
if system_prompt_extras.is_empty() {
let sanitized_system_prompt_extras: Vec<String> = system_prompt_extras
.into_iter()
.map(|extra| sanitize_unicode_tags(&extra))
.collect();
if sanitized_system_prompt_extras.is_empty() {
base_prompt
} else {
format!(
"{}\n\n# Additional Instructions:\n\n{}",
base_prompt,
system_prompt_extras.join("\n\n")
sanitized_system_prompt_extras.join("\n\n")
)
}
}
@@ -221,4 +238,93 @@ mod tests {
"system.md"
);
}
#[test]
fn test_build_system_prompt_sanitizes_override() {
let mut manager = PromptManager::new();
let malicious_override = "System prompt\u{E0041}\u{E0042}\u{E0043}with hidden text";
manager.set_system_prompt_override(malicious_override.to_string());
let result =
manager.build_system_prompt(vec![], None, Value::String("".to_string()), None, None);
assert!(!result.contains('\u{E0041}'));
assert!(!result.contains('\u{E0042}'));
assert!(!result.contains('\u{E0043}'));
assert!(result.contains("System prompt"));
assert!(result.contains("with hidden text"));
}
#[test]
fn test_build_system_prompt_sanitizes_extras() {
let mut manager = PromptManager::new();
let malicious_extra = "Extra instruction\u{E0041}\u{E0042}\u{E0043}hidden";
manager.add_system_prompt_extra(malicious_extra.to_string());
let result =
manager.build_system_prompt(vec![], None, Value::String("".to_string()), None, None);
assert!(!result.contains('\u{E0041}'));
assert!(!result.contains('\u{E0042}'));
assert!(!result.contains('\u{E0043}'));
assert!(result.contains("Extra instruction"));
assert!(result.contains("hidden"));
}
#[test]
fn test_build_system_prompt_sanitizes_multiple_extras() {
let mut manager = PromptManager::new();
manager.add_system_prompt_extra("First\u{E0041}instruction".to_string());
manager.add_system_prompt_extra("Second\u{E0042}instruction".to_string());
manager.add_system_prompt_extra("Third\u{E0043}instruction".to_string());
let result =
manager.build_system_prompt(vec![], None, Value::String("".to_string()), None, None);
assert!(!result.contains('\u{E0041}'));
assert!(!result.contains('\u{E0042}'));
assert!(!result.contains('\u{E0043}'));
assert!(result.contains("Firstinstruction"));
assert!(result.contains("Secondinstruction"));
assert!(result.contains("Thirdinstruction"));
}
#[test]
fn test_build_system_prompt_preserves_legitimate_unicode_in_extras() {
let mut manager = PromptManager::new();
let legitimate_unicode = "Instruction with 世界 and 🌍 emojis";
manager.add_system_prompt_extra(legitimate_unicode.to_string());
let result =
manager.build_system_prompt(vec![], None, Value::String("".to_string()), None, None);
assert!(result.contains("世界"));
assert!(result.contains("🌍"));
assert!(result.contains("Instruction with"));
assert!(result.contains("emojis"));
}
#[test]
fn test_build_system_prompt_sanitizes_extension_instructions() {
let manager = PromptManager::new();
let malicious_extension_info = ExtensionInfo::new(
"test_extension",
"Extension help\u{E0041}\u{E0042}\u{E0043}hidden instructions",
false,
);
let result = manager.build_system_prompt(
vec![malicious_extension_info],
None,
Value::String("".to_string()),
None,
None,
);
assert!(!result.contains('\u{E0041}'));
assert!(!result.contains('\u{E0042}'));
assert!(!result.contains('\u{E0043}'));
assert!(result.contains("Extension help"));
assert!(result.contains("hidden instructions"));
}
}