mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-30 07:33:59 +08:00
fix(cli): avoid panic on multi-byte UTF-8 in --since duration (#2446)
parse_duration_to_ms was moved to commands/common.rs since the original PR was opened, but it still split the last byte of the input with split_at(s.len() - 1), which panics when the final character is multi-byte UTF-8 (e.g. 'openshell logs my-sandbox --since 5€'). Split off the last character using its UTF-8 length instead, so invalid units surface the intended 'unknown duration unit' error. Add regression tests in commands/common.rs. Signed-off-by: Andrew White <andrewh@cdw.com>
This commit is contained in:
@@ -719,7 +719,10 @@ pub fn parse_duration_to_ms(s: &str) -> Result<i64> {
|
||||
if s.is_empty() {
|
||||
return Err(miette::miette!("empty duration string"));
|
||||
}
|
||||
let (num_str, unit) = s.split_at(s.len() - 1);
|
||||
// Split off the last character by its UTF-8 length: indexing by byte
|
||||
// length would panic on multi-byte units (e.g. "5\u{20ac}").
|
||||
let last_len = s.chars().last().map_or(0, char::len_utf8);
|
||||
let (num_str, unit) = s.split_at(s.len() - last_len);
|
||||
let num: i64 = num_str
|
||||
.parse()
|
||||
.map_err(|_| miette::miette!("invalid duration: {s} (expected e.g. 5m, 1h, 30s)"))?;
|
||||
@@ -948,3 +951,28 @@ pub fn scrub_git_env(command: &mut Command) -> &mut Command {
|
||||
}
|
||||
command
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_duration_to_ms_parses_supported_units() {
|
||||
assert_eq!(parse_duration_to_ms("30s").expect("parse"), 30_000);
|
||||
assert_eq!(parse_duration_to_ms("5m").expect("parse"), 300_000);
|
||||
assert_eq!(parse_duration_to_ms("1h").expect("parse"), 3_600_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_duration_to_ms_rejects_multi_byte_unit_without_panicking() {
|
||||
let err = parse_duration_to_ms("5\u{20ac}").expect_err("multi-byte unit should error");
|
||||
assert!(err.to_string().contains("unknown duration unit"));
|
||||
|
||||
let err = parse_duration_to_ms("\u{20ac}").expect_err("missing number should error");
|
||||
assert!(err.to_string().contains("invalid duration"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user