diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index 77febdaaa41..2c21d93f2fb 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -159,6 +159,17 @@ fn parse_number_part(s: &str, input: &str) -> Result { return Err(translate!("numfmt-error-invalid-number", "input" => input.quote())); } + // GNU numfmt does not accept a leading '+' sign nor scientific notation, but + // Rust's i128/f64 parsers do, so "+5" and "1e-3" would slip through. Reject + // them here, mirroring GNU's two distinct diagnostics (a leading sign is an + // "invalid number", an exponent is an "invalid suffix in input"). + if s.starts_with('+') { + return Err(translate!("numfmt-error-invalid-number", "input" => input.quote())); + } + if s.bytes().any(|b| b == b'e' || b == b'E') { + return Err(translate!("numfmt-error-invalid-suffix", "input" => input.quote())); + } + if let Ok(n) = s.parse::() { return Ok(ParsedNumber::ExactInt(n)); } diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index 33d810af269..2227dad61dc 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -185,6 +185,26 @@ fn test_header_error_if_negative() { .stderr_is("numfmt: invalid header value '-3'\n"); } +#[test] +fn test_reject_leading_plus_and_scientific_notation() { + // GNU numfmt rejects a leading '+' as an invalid number and rejects scientific + // notation as an invalid suffix; Rust's parsers accept both, so guard against it. + for input in ["+5", "+5K", "+1e-3"] { + new_ucmd!() + .arg("--from=auto") + .pipe_in(format!("{input}\n")) + .fails_with_code(2) + .stderr_is(format!("numfmt: invalid number: '{input}'\n")); + } + for input in ["1e-3", "1e+3", "5e-5", "1.0e-2", "-1e-5"] { + new_ucmd!() + .arg("--from=auto") + .pipe_in(format!("{input}\n")) + .fails_with_code(2) + .stderr_is(format!("numfmt: invalid suffix in input: '{input}'\n")); + } +} + #[test] fn test_negative() { new_ucmd!()