mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 01:47:08 -06:00
fix(cms): reject malformed hex data in .look LUTs (#8657)
Co-authored-by: Lucenx9 <185146821+Lucenx9@users.noreply.github.com>
This commit is contained in:
@@ -221,7 +221,14 @@ pub fn parse_look(text: &str) -> Result<LutFile, LutError> {
|
||||
};
|
||||
let size: usize = tag("size").and_then(|s| s.parse().ok()).ok_or_else(|| LutError("no <size> in .look".into()))?;
|
||||
let (entries, _) = checked_data_len(size)?;
|
||||
let hex: Vec<u8> = tag("data").ok_or_else(|| LutError("no <data> in .look".into()))?.bytes().filter(u8::is_ascii_hexdigit).collect();
|
||||
let hex: Vec<u8> = tag("data").ok_or_else(|| LutError("no <data> in .look".into()))?.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
|
||||
if hex.iter().any(|b| !b.is_ascii_hexdigit()) {
|
||||
return err("invalid hexadecimal <data> in .look");
|
||||
}
|
||||
// Each float needs eight hex digits; as_chunks would silently discard a partial value.
|
||||
if !hex.len().is_multiple_of(8) {
|
||||
return err("incomplete float in .look <data>");
|
||||
}
|
||||
let nib = |c: u8| (c as char).to_digit(16).unwrap_or(0) as u8;
|
||||
let bytes: Vec<u8> = hex.as_chunks::<2>().0.iter().map(|p| nib(p[0]) << 4 | nib(p[1])).collect();
|
||||
let floats: Vec<f32> = bytes.as_chunks::<4>().0.iter().map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])).collect();
|
||||
@@ -456,6 +463,38 @@ DOMAIN_MAX 0.5 1 1
|
||||
assert_eq!(parse("a.look", xml.as_bytes()).unwrap().data, id.data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn look_rgb_and_rgba_allow_whitespace_in_hex_data() {
|
||||
let id = LutFile::identity(2);
|
||||
for channels in [3, 4] {
|
||||
let mut hex = String::new();
|
||||
for rgb in id.data.as_chunks::<3>().0 {
|
||||
for value in rgb.iter().copied().chain((channels == 4).then_some(1.0)) {
|
||||
for byte in value.to_le_bytes() {
|
||||
hex.push_str(&format!("{byte:02x} \t\r\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
let xml = format!("<look><size>2</size><data>\"{hex}\"</data></look>");
|
||||
assert_eq!(parse("valid.look", xml.as_bytes()).unwrap().data, id.data);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn look_rejects_partial_floats_and_invalid_hex() {
|
||||
let id = LutFile::identity(2);
|
||||
let hex: String = id.data.iter().flat_map(|v| v.to_le_bytes()).map(|b| format!("{b:02X}")).collect();
|
||||
for suffix in ["0", "00", "0000", "000000", "G", "é"] {
|
||||
let xml = format!("<look><size>2</size><data>{hex}{suffix}</data></look>");
|
||||
assert!(parse("bad.look", xml.as_bytes()).is_err(), "accepted malformed suffix {suffix:?}");
|
||||
}
|
||||
let invalid = hex.replacen('0', "G", 1);
|
||||
let xml = format!("<look><size>2</size><data>{invalid}</data></look>");
|
||||
assert!(parse("bad.look", xml.as_bytes()).unwrap_err().0.contains("invalid hexadecimal"));
|
||||
let xml = format!("<look><size>2</size><data>{hex}00</data></look>");
|
||||
assert!(parse("bad.look", xml.as_bytes()).unwrap_err().0.contains("incomplete float"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn look_3d_size_exponent_overflow_is_rejected() {
|
||||
let text = format!("<look><size>{}</size><data>00</data></look>", usize::MAX);
|
||||
|
||||
Reference in New Issue
Block a user