Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 54 additions & 27 deletions src/cortex-cli/src/import_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,31 +357,47 @@ fn validate_export_messages(messages: &[ExportMessage]) -> Result<()> {
for (idx, message) in messages.iter().enumerate() {
// Check for base64-encoded image data in content
// Common pattern: "data:image/png;base64,..." or "data:image/jpeg;base64,..."
if let Some(data_uri_start) = message.content.find("data:image/")
&& let Some(base64_marker) = message.content[data_uri_start..].find(";base64,")
{
let base64_start = data_uri_start + base64_marker + 8; // 8 = len(";base64,")
let remaining = &message.content[base64_start..];

// Find end of base64 data (could end with quote, whitespace, or end of string)
let base64_end = remaining
.find(['"', '\'', ' ', '\n', ')'])
.unwrap_or(remaining.len());
let base64_data = &remaining[..base64_end];

// Validate the base64 data
if !base64_data.is_empty() {
let engine = base64::engine::general_purpose::STANDARD;
if let Err(e) = engine.decode(base64_data) {
bail!(
"Invalid base64 encoding in message {} (role: '{}'): {}\n\
The image data starting at position {} has invalid base64 encoding.\n\
Please ensure all embedded images use valid base64 encoding.",
idx + 1,
message.role,
e,
data_uri_start
);
if let Some(data_uri_start) = message.content.find("data:image/") {
// Use safe slicing with .get() to avoid panics on multi-byte UTF-8 boundaries
let content_after_start = match message.content.get(data_uri_start..) {
Some(s) => s,
None => continue, // Invalid byte offset, skip this message
};

if let Some(base64_marker) = content_after_start.find(";base64,") {
let base64_start = data_uri_start + base64_marker + 8; // 8 = len(";base64,")

// Safe slicing for the remaining content after base64 marker
let remaining = match message.content.get(base64_start..) {
Some(s) => s,
None => continue, // Invalid byte offset, skip this message
};

// Find end of base64 data (could end with quote, whitespace, or end of string)
let base64_end = remaining
.find(['"', '\'', ' ', '\n', ')'])
.unwrap_or(remaining.len());

// Safe slicing for the base64 data
let base64_data = match remaining.get(..base64_end) {
Some(s) => s,
None => continue, // Invalid byte offset, skip this message
};

// Validate the base64 data
if !base64_data.is_empty() {
let engine = base64::engine::general_purpose::STANDARD;
if let Err(e) = engine.decode(base64_data) {
bail!(
"Invalid base64 encoding in message {} (role: '{}'): {}\n\
The image data starting at position {} has invalid base64 encoding.\n\
Please ensure all embedded images use valid base64 encoding.",
idx + 1,
message.role,
e,
data_uri_start
);
}
}
}
}
Expand All @@ -395,13 +411,24 @@ fn validate_export_messages(messages: &[ExportMessage]) -> Result<()> {
// Try to find and validate any base64 in the arguments
for (pos, _) in args_str.match_indices(";base64,") {
let base64_start = pos + 8;
let remaining = &args_str[base64_start..];

// Safe slicing for the remaining content after base64 marker
let remaining = match args_str.get(base64_start..) {
Some(s) => s,
None => continue, // Invalid byte offset, skip this occurrence
};

let base64_end = remaining
.find(|c: char| {
c == '"' || c == '\'' || c == ' ' || c == '\n' || c == ')'
})
.unwrap_or(remaining.len());
let base64_data = &remaining[..base64_end];

// Safe slicing for the base64 data
let base64_data = match remaining.get(..base64_end) {
Some(s) => s,
None => continue, // Invalid byte offset, skip this occurrence
};

if !base64_data.is_empty() {
let engine = base64::engine::general_purpose::STANDARD;
Expand Down
9 changes: 8 additions & 1 deletion src/cortex-cli/src/utils/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,14 @@ pub fn send_task_notification(session_id: &str, success: bool) -> Result<()> {
"Cortex Task Failed"
};

let short_id = &session_id[..8.min(session_id.len())];
// Use safe UTF-8 slicing - find the last valid char boundary at or before position 8
let short_id = session_id
.char_indices()
.take_while(|(idx, _)| *idx < 8)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic bug: take_while(|(idx, _)| *idx < 8) takes chars whose start index is <8, but doesn't check if they exceed 8 bytes when included. If a 4-byte char starts at index 7, it extends to byte 11.

Suggested change
.take_while(|(idx, _)| *idx < 8)
.take_while(|(idx, ch)| idx + ch.len_utf8() <= 8)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cortex-cli/src/utils/notification.rs
Line: 69:69

Comment:
Logic bug: `take_while(|(idx, _)| *idx < 8)` takes chars whose start index is <8, but doesn't check if they exceed 8 bytes when included. If a 4-byte char starts at index 7, it extends to byte 11.

```suggestion
        .take_while(|(idx, ch)| idx + ch.len_utf8() <= 8)
```

How can I resolve this? If you propose a fix, please make it concise.

.map(|(idx, ch)| idx + ch.len_utf8())
.last()
.and_then(|end| session_id.get(..end))
.unwrap_or(session_id);
let body = format!("Session: {}", short_id);

let urgency = if success {
Expand Down
Loading