Skip to content

Commit 912acd7

Browse files
committed
fix(cli): use safe UTF-8 slicing in import command base64 extraction
1 parent d201070 commit 912acd7

1 file changed

Lines changed: 54 additions & 27 deletions

File tree

src/cortex-cli/src/import_cmd.rs

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -357,31 +357,47 @@ fn validate_export_messages(messages: &[ExportMessage]) -> Result<()> {
357357
for (idx, message) in messages.iter().enumerate() {
358358
// Check for base64-encoded image data in content
359359
// Common pattern: "data:image/png;base64,..." or "data:image/jpeg;base64,..."
360-
if let Some(data_uri_start) = message.content.find("data:image/")
361-
&& let Some(base64_marker) = message.content[data_uri_start..].find(";base64,")
362-
{
363-
let base64_start = data_uri_start + base64_marker + 8; // 8 = len(";base64,")
364-
let remaining = &message.content[base64_start..];
365-
366-
// Find end of base64 data (could end with quote, whitespace, or end of string)
367-
let base64_end = remaining
368-
.find(['"', '\'', ' ', '\n', ')'])
369-
.unwrap_or(remaining.len());
370-
let base64_data = &remaining[..base64_end];
371-
372-
// Validate the base64 data
373-
if !base64_data.is_empty() {
374-
let engine = base64::engine::general_purpose::STANDARD;
375-
if let Err(e) = engine.decode(base64_data) {
376-
bail!(
377-
"Invalid base64 encoding in message {} (role: '{}'): {}\n\
378-
The image data starting at position {} has invalid base64 encoding.\n\
379-
Please ensure all embedded images use valid base64 encoding.",
380-
idx + 1,
381-
message.role,
382-
e,
383-
data_uri_start
384-
);
360+
if let Some(data_uri_start) = message.content.find("data:image/") {
361+
// Use safe slicing with .get() to avoid panics on multi-byte UTF-8 boundaries
362+
let content_after_start = match message.content.get(data_uri_start..) {
363+
Some(s) => s,
364+
None => continue, // Invalid byte offset, skip this message
365+
};
366+
367+
if let Some(base64_marker) = content_after_start.find(";base64,") {
368+
let base64_start = data_uri_start + base64_marker + 8; // 8 = len(";base64,")
369+
370+
// Safe slicing for the remaining content after base64 marker
371+
let remaining = match message.content.get(base64_start..) {
372+
Some(s) => s,
373+
None => continue, // Invalid byte offset, skip this message
374+
};
375+
376+
// Find end of base64 data (could end with quote, whitespace, or end of string)
377+
let base64_end = remaining
378+
.find(['"', '\'', ' ', '\n', ')'])
379+
.unwrap_or(remaining.len());
380+
381+
// Safe slicing for the base64 data
382+
let base64_data = match remaining.get(..base64_end) {
383+
Some(s) => s,
384+
None => continue, // Invalid byte offset, skip this message
385+
};
386+
387+
// Validate the base64 data
388+
if !base64_data.is_empty() {
389+
let engine = base64::engine::general_purpose::STANDARD;
390+
if let Err(e) = engine.decode(base64_data) {
391+
bail!(
392+
"Invalid base64 encoding in message {} (role: '{}'): {}\n\
393+
The image data starting at position {} has invalid base64 encoding.\n\
394+
Please ensure all embedded images use valid base64 encoding.",
395+
idx + 1,
396+
message.role,
397+
e,
398+
data_uri_start
399+
);
400+
}
385401
}
386402
}
387403
}
@@ -395,13 +411,24 @@ fn validate_export_messages(messages: &[ExportMessage]) -> Result<()> {
395411
// Try to find and validate any base64 in the arguments
396412
for (pos, _) in args_str.match_indices(";base64,") {
397413
let base64_start = pos + 8;
398-
let remaining = &args_str[base64_start..];
414+
415+
// Safe slicing for the remaining content after base64 marker
416+
let remaining = match args_str.get(base64_start..) {
417+
Some(s) => s,
418+
None => continue, // Invalid byte offset, skip this occurrence
419+
};
420+
399421
let base64_end = remaining
400422
.find(|c: char| {
401423
c == '"' || c == '\'' || c == ' ' || c == '\n' || c == ')'
402424
})
403425
.unwrap_or(remaining.len());
404-
let base64_data = &remaining[..base64_end];
426+
427+
// Safe slicing for the base64 data
428+
let base64_data = match remaining.get(..base64_end) {
429+
Some(s) => s,
430+
None => continue, // Invalid byte offset, skip this occurrence
431+
};
405432

406433
if !base64_data.is_empty() {
407434
let engine = base64::engine::general_purpose::STANDARD;

0 commit comments

Comments
 (0)