Apply review fixes

- crates/format/src/lib.rs
This commit is contained in:
aiongg committed 2026-11-03 18:35:58 +00:00
1 parent f67f052b7f
commit b7d83dbd07
1 file changed
+27 -8
+27 -8
View File
@@ -180,8 +180,9 @@ const MAX_ENTRIES: usize = 100_000;
/// How much a `.designcraft` archive may expand to when read. The sizes an archive claims are
/// not trusted, so deflated entries are capped; a *stored* entry can't expand (its size is its
/// bytes in the archive, which is already in memory, and all stored entries read together are held to the archive's own size), so a placed image — stored as is by [`save`] — opens whatever its size
/// and doesn't count against the budget. Saving has no cap, and this keeps every saved
/// bytes in the archive, which is already in memory, and all stored entries read together are
/// held to the archive's own size), so a placed image — stored as is by [`save`] — opens whatever
/// its size and doesn't count against the budget. Saving has no cap, and this keeps every saved
/// document openable (#2166).
#[derive(Clone, Copy, Debug)]
struct Limits {
@@ -200,11 +201,24 @@ impl Limits {
/// Read entry `name` of `z`, or `None` when it is missing or unreadable. Refuses a deflated entry
/// that expands past `limit` bytes or past what is `left` of the archive's budget, without
/// decompressing more than that: the sizes the archive claims are not trusted. A stored entry
/// is read at its size in the archive and charged to `stored_left`, the archive's own size.
fn read_entry(z: &mut zip::ZipArchive<Cursor<&[u8]>>, name: &str, limit: u64, left: &mut u64, stored_left: &mut u64) -> Result<Option<Vec<u8>>, FormatError> {
/// is read at its size in the archive and charged to `stored_left`, the archive's own size; only a
/// stored asset is free of `limit`.
fn read_entry(
z: &mut zip::ZipArchive<Cursor<&[u8]>>,
name: &str,
limit: u64,
left: &mut u64,
stored_left: &mut u64,
) -> Result<Option<Vec<u8>>, FormatError> {
let Ok(f) = z.by_name(name) else { return Ok(None) };
let stored = f.compression() == zip::CompressionMethod::Stored;
let limit = if stored { f.compressed_size().min(*stored_left) } else { limit.min(*left) };
let limit = if !stored {
limit.min(*left)
} else if name.starts_with("assets/") {
f.compressed_size().min(*stored_left)
} else {
f.compressed_size().min(*stored_left).min(limit)
};
let too_big = || FormatError::Io(format!("{name} is larger than the {limit}-byte limit (or the archive expands past its {}-byte budget)", *left));
if f.size() > limit {
return Err(too_big());
@@ -235,7 +249,8 @@ fn load_zip_within(bytes: &[u8], limits: Limits) -> Result<Document, FormatError
return Err(FormatError::NotOurs(format!("{} entries (at most {MAX_ENTRIES})", z.len())));
}
let mut left = limits.total;
// Stored entries can't expand, but a crafted zip can point several at the same bytes: together they get the archive's own size.
// Stored entries can't expand, but a crafted zip can point several at the same bytes: together
// they get the archive's own size.
let mut stored_left = bytes.len() as u64;
let version = read_entry(&mut z, "meta.json", limits.json, &mut left, &mut stored_left)?
.and_then(|meta| serde_json::from_slice::<Value>(&meta).ok())
@@ -244,7 +259,8 @@ fn load_zip_within(bytes: &[u8], limits: Limits) -> Result<Document, FormatError
if version > u64::from(VERSION) {
return Err(FormatError::TooNew(version));
}
let json = read_entry(&mut z, "document.json", limits.json, &mut left, &mut stored_left)?.ok_or_else(|| FormatError::NotOurs("missing document.json".into()))?;
let json = read_entry(&mut z, "document.json", limits.json, &mut left, &mut stored_left)?
.ok_or_else(|| FormatError::NotOurs("missing document.json".into()))?;
let mut v: Value = serde_json::from_slice(&json).map_err(|e| FormatError::NotOurs(e.to_string()))?;
upgrade_json(&mut v);
let mut doc: Document = serde_json::from_value(v).map_err(|e| FormatError::NotOurs(e.to_string()))?;
@@ -260,7 +276,10 @@ fn load_zip_within(bytes: &[u8], limits: Limits) -> Result<Document, FormatError
if !doc.assets.contains_key(&AssetId(id)) {
continue;
}
if let (Some(data), Some(a)) = (read_entry(&mut z, &name, limits.asset, &mut left, &mut stored_left)?, doc.assets.get_mut(&AssetId(id))) {
if let (Some(data), Some(a)) = (
read_entry(&mut z, &name, limits.asset, &mut left, &mut stored_left)?,
doc.assets.get_mut(&AssetId(id)),
) {
let same = loaded.entry(fingerprint.hash_one(data.as_slice())).or_default();
let data = match same.iter().find(|d| ***d == data) {
Some(d) => d.clone(),