Fix UTF-8 unsafe truncation in WASM emit_message (#1015)

Co-authored-by: Lawyered <[email protected]>
This commit is contained in:
Zaki Manian
2026-03-12 11:10:31 -07:00
committed by GitHub
co-authored by Lawyered
parent c26f116a98
commit 0b81342b5c
+26 -2
View File
@@ -63,7 +63,11 @@ const ALLOWED_MIME_PREFIXES: &[&str] = &[
"application/x-tar",
"application/octet-stream",
];
/// Truncate a string to at most `max_bytes` without splitting UTF-8 code points.
fn truncate_utf8(s: &str, max_bytes: usize) -> &str {
let end = crate::util::floor_char_boundary(s, max_bytes);
&s[..end]
}
/// A message emitted by a WASM channel to be sent to the agent.
#[derive(Debug, Clone)]
pub struct EmittedMessage {
@@ -264,7 +268,7 @@ impl ChannelHostState {
max = MAX_MESSAGE_CONTENT_SIZE,
"Message content too large, truncating"
);
let mut truncated = msg.content[..MAX_MESSAGE_CONTENT_SIZE].to_string();
let mut truncated = truncate_utf8(&msg.content, MAX_MESSAGE_CONTENT_SIZE).to_string();
truncated.push_str("... (truncated)");
let msg = EmittedMessage {
content: truncated,
@@ -631,6 +635,7 @@ mod tests {
use crate::channels::wasm::host::{
Attachment, ChannelEmitRateLimiter, ChannelHostState, EmittedMessage,
MAX_ATTACHMENT_TOTAL_SIZE, MAX_ATTACHMENTS_PER_MESSAGE, MAX_EMITS_PER_EXECUTION,
MAX_MESSAGE_CONTENT_SIZE,
};
#[test]
@@ -689,6 +694,25 @@ mod tests {
assert_eq!(state.emits_dropped(), 1);
}
#[test]
fn test_emit_message_truncates_utf8_safely() {
let caps = ChannelCapabilities::for_channel("test");
let mut state = ChannelHostState::new("test", caps);
let prefix = "a".repeat(MAX_MESSAGE_CONTENT_SIZE - 1);
let content = format!("{}🙂suffix", prefix);
let msg = EmittedMessage::new("user123", content);
state.emit_message(msg).unwrap();
let messages = state.take_emitted_messages();
assert_eq!(messages.len(), 1);
let emitted = &messages[0].content;
assert!(emitted.starts_with(&prefix));
assert!(emitted.ends_with("... (truncated)"));
assert!(!emitted.contains("🙂"));
}
#[test]
fn test_workspace_write_prefixing() {
let caps = ChannelCapabilities::for_channel("slack");