From dd119ca0074c9ae5324f38c2b8e08c8e84b0c4f4 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sun, 25 Oct 2026 06:01:52 +0000 Subject: [PATCH] Reject signed strings in Hash128::parse Found while testing crates/preview/src/hash.rs:21 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/preview/src/hash.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/preview/src/hash.rs b/crates/preview/src/hash.rs index e3ab51fe8..63650fd16 100644 --- a/crates/preview/src/hash.rs +++ b/crates/preview/src/hash.rs @@ -18,7 +18,8 @@ impl fmt::Display for Hash128 { impl Hash128 { pub fn parse(s: &str) -> Option { - (s.len() == 32).then(|| u128::from_str_radix(s, 16).ok().map(Hash128)).flatten() + // `from_str_radix` also accepts a leading '+', so check the digits explicitly. + (s.len() == 32 && s.bytes().all(|b| b.is_ascii_hexdigit())).then(|| u128::from_str_radix(s, 16).ok().map(Hash128)).flatten() } } @@ -59,3 +60,16 @@ impl Hasher128 { pub fn hash_bytes(bytes: &[u8]) -> Hash128 { Hasher128::new().update(bytes).finish() } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_rejects_signed_and_non_hex() { + assert_eq!(Hash128::parse(&format!("+{}", "1".repeat(31))), None); + assert_eq!(Hash128::parse(&format!("-{}", "1".repeat(31))), None); + let h = "0123456789abcdefABCDEF0123456789"; + assert_eq!(Hash128::parse(h).map(|x| x.to_string()), Some(h.to_lowercase())); + } +}