mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-05 18:19:57 +00:00
Apply review fixes
- crates/format/src/lib.rs
This commit is contained in:
1 parent
f67f052b7f
commit
b7d83dbd07
1 file changed
+27
-8
@@ -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(),
|
||||
|
||||
Reference in new issue
Block a user