diff --git a/crates/color/src/lib.rs b/crates/color/src/lib.rs index ce3340162..0c271a3a8 100644 --- a/crates/color/src/lib.rs +++ b/crates/color/src/lib.rs @@ -296,7 +296,11 @@ pub fn hsl_to_rgb(h: f32, s: f32, l: f32) -> [f32; 3] { /// Parse `#rrggbb` / `#rrggbbaa` into 0..1 floats. pub fn parse_hex(s: &str) -> Option<[f32; 4]> { - let s = s.trim().trim_start_matches('#'); + let s = s.trim(); + let s = s.strip_prefix('#').unwrap_or(s); + if !s.bytes().all(|b| b.is_ascii_hexdigit()) { + return None; + } let b = |i: usize| u8::from_str_radix(s.get(i..i + 2)?, 16).ok().map(|v| v as f32 / 255.0); match s.len() { 6 => Some([b(0)?, b(2)?, b(4)?, 1.0]), @@ -318,6 +322,16 @@ pub fn to_hex(c: [f32; 4]) -> String { mod tests { use super::*; + #[test] + fn parse_hex_rejects_signs() { + assert_eq!(parse_hex("#+f+f+f"), None); + assert_eq!(parse_hex("#-f-f-f"), None); + assert_eq!(parse_hex("##0f0f0f"), None); + assert_eq!(parse_hex("#0f0f0"), None); + assert!(parse_hex("#0f0f0f").is_some()); + assert!(parse_hex("0f0f0f80").is_some()); + } + #[test] fn ycbcr_roundtrip() { for m in [Matrix::Bt601, Matrix::Bt709, Matrix::Bt2020Ncl] { diff --git a/crates/engine/src/captions.rs b/crates/engine/src/captions.rs index d048a5677..02bddd456 100644 --- a/crates/engine/src/captions.rs +++ b/crates/engine/src/captions.rs @@ -82,7 +82,11 @@ fn parse_color(v: &Value) -> Option<[u8; 4]> { _ => None, }; } - let h = v.as_str()?.trim_start_matches('#'); + let h = v.as_str()?; + let h = h.strip_prefix('#').unwrap_or(h); + if !h.bytes().all(|b| b.is_ascii_hexdigit()) { + return None; + } let b = |i: usize| u8::from_str_radix(h.get(i..i + 2)?, 16).ok(); match h.len() { 6 => Some([b(0)?, b(2)?, b(4)?, 255]),