diff --git a/crates/font-store/src/language_packs.rs b/crates/font-store/src/language_packs.rs index 1e3fbd882..065ab2784 100644 --- a/crates/font-store/src/language_packs.rs +++ b/crates/font-store/src/language_packs.rs @@ -126,10 +126,22 @@ struct Installed { version: u32, packs: Vec, /// Committed backup IDs, recorded atomically with removing their pack entries. - #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[serde(default, deserialize_with = "read_receipts", skip_serializing_if = "Vec::is_empty")] committed_removals: Vec, } +/// Decode optional receipt entries independently from the readable pack metadata. Record size +/// and JSON nesting remain bounded by the existing reader and serde_json's recursion limit. +fn read_receipts<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + let entries = serde_json::Value::deserialize(deserializer)?; + Ok(match entries { + serde_json::Value::Array(entries) => { + entries.into_iter().filter_map(|entry| if let serde_json::Value::String(id) = entry { Some(id) } else { None }).collect() + } + _ => Vec::new(), + }) +} + impl PackIndex { /// Parses and checks an index: format 1, every locale a lowercase tag and listed once, every /// archive at most [`MAX_PACK_ARCHIVE_BYTES`] with a sha256. Archive URLs must be `https://` diff --git a/crates/font-store/src/language_packs/tests.rs b/crates/font-store/src/language_packs/tests.rs index 5e468bc4b..3bf8922cc 100644 --- a/crates/font-store/src/language_packs/tests.rs +++ b/crates/font-store/src/language_packs/tests.rs @@ -667,7 +667,7 @@ fn residual_one_bad_receipt_preserves_valid_metadata_and_cleanup() { std::fs::create_dir_all(parent.join("11-12")).unwrap(); std::fs::write(parent.join("11-12/keep"), b"committed").unwrap(); let mut raw = serde_json::to_value(&original).unwrap(); - raw["committed_removals"] = serde_json::json!(["11-12", "../outside"]); + raw["committed_removals"] = serde_json::json!(["11-12", "../outside", null, 42, true, {}, ["99-99"]]); std::fs::write(store.dir().join(INSTALLED), serde_json::to_vec(&raw).unwrap()).unwrap(); let record = store.read_record().expect("one invalid receipt must not invalidate the whole record"); assert_eq!(record.committed_removals, ["11-12"]);