mirror of
https://github.com/storytold/soundcraft.git
synced 2026-11-03 00:41:02 -06:00
Bundles: refuse entries that share their data (#1303)
Bundle entries are stored, so the files read out of one can only outgrow the bundle when several entries point at the same bytes. A small bundle naming one payload thousands of times was copied out once per name, up to the 1 GiB total limit, in the browser. Check the archive for overlapping entry data before reading any entry.
This commit is contained in:
@@ -734,6 +734,12 @@ fn read_bundle(bytes: &[u8], limits: BundleLimits) -> Result<(String, std::colle
|
||||
if z.len() > limits.entries {
|
||||
return Err(EngineError::Io("bundle: too many files".into()));
|
||||
}
|
||||
// Entries are stored, so the files we keep can only outgrow the bundle when several entries
|
||||
// point at the same bytes: a small bundle naming one payload thousands of times would be
|
||||
// copied out once per name.
|
||||
if z.has_overlapping_files().map_err(zerr)? {
|
||||
return Err(EngineError::Io("bundle: files share data".into()));
|
||||
}
|
||||
let (mut text, mut files, mut total) = (None, std::collections::HashMap::new(), 0u64);
|
||||
for i in 0..z.len() {
|
||||
let mut f = z.by_index(i).map_err(zerr)?;
|
||||
|
||||
@@ -194,6 +194,23 @@ mod bundle_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entries_sharing_one_payload_are_refused() {
|
||||
use std::io::Write as _;
|
||||
let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
|
||||
let opts = zip::write::SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
|
||||
w.start_file("Song.scraft", opts).unwrap();
|
||||
w.write_all(Session::default().to_json().unwrap().as_bytes()).unwrap();
|
||||
w.start_file("Audio Files/a.wav", opts).unwrap();
|
||||
w.write_all(&[7u8; 4096]).unwrap();
|
||||
for i in 0..100 {
|
||||
w.shallow_copy_file("Audio Files/a.wav", &format!("Audio Files/copy{i}.wav")).unwrap();
|
||||
}
|
||||
let bundle = w.finish().unwrap().into_inner();
|
||||
let err = read_bundle(&bundle, BUNDLE_LIMITS).unwrap_err();
|
||||
assert!(err.to_string().contains("share data"), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_bundle_is_saved_only_if_it_opens_again() {
|
||||
// #433: the writer keeps to the reader's limits.
|
||||
|
||||
Reference in New Issue
Block a user