From 1bd835b55bc490e30b118c59da36104a5a48e336 Mon Sep 17 00:00:00 2001 From: Wade Tregaskis Date: Fri, 12 Jun 2026 07:43:06 -0700 Subject: [PATCH] fix: compound ttl durations silently parsing as tiny values The suffix duration parser took the leading digit run as the value and the last character as the unit, ignoring everything in between, so a natural compound spec like 'shpool attach --ttl 1h30m' parsed as one MINUTE: the reaper killed the session 60 seconds in and the user lost their work. '2d12h' similarly meant 2 hours, '12.5h' meant 12 hours. Require the unit character to directly follow the digits and end the string, so unsupported compound formats are rejected with an error instead of silently misinterpreted. --- libshpool/src/duration.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libshpool/src/duration.rs b/libshpool/src/duration.rs index 076dfec3..fe2a3ab8 100644 --- a/libshpool/src/duration.rs +++ b/libshpool/src/duration.rs @@ -64,6 +64,12 @@ fn parse_colon_duration(src: &str) -> anyhow::Result { fn parse_suffix_duration(src: &str) -> anyhow::Result { let num: String = src.chars().take_while(|c| c.is_numeric()).collect(); let c = src.chars().last().ok_or(anyhow!("internal error: no suffix"))?; + // The unit must directly follow the number and end the string, + // otherwise compound durations like "1h30m" would silently parse + // as 1 minute. + if src.chars().count() != num.chars().count() + 1 { + bail!("could not parse '{}' as duration", src); + } make_suffix_duration(num.parse::().context("parsing num part of duration")?, c) .ok_or(anyhow!("unknown time unit '{}'", c)) } @@ -113,6 +119,12 @@ mod test { ("12x", "unknown time unit"), (":1", "parsing minutes part"), ("1:1:1:1:1", "cannot have more than 4"), + // compound durations are not supported and must not silently + // parse as just their first number + last unit + ("1h30m", "could not parse"), + ("2d12h", "could not parse"), + ("12.5h", "could not parse"), + ("5x7d", "could not parse"), ]; for (src, err_substring) in cases.into_iter() {