{"record":{"id":"463bcb14bbeb8a48","repo":"zeroclaw-labs/zeroclaw","slug":"plugin-archive-exceeds-extracted-size-limit-of-ma","errorCode":null,"errorMessage":"plugin archive exceeds extracted size limit of {max_extracted_bytes} bytes","messagePattern":"plugin archive exceeds extracted size limit of (.+?) bytes","errorType":"console","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/plugin_registry.rs","lineNumber":192,"sourceCode":"where\n    R: Read + Seek,\n{\n    let mut archive = zip::ZipArchive::new(reader)?;\n    std::fs::create_dir_all(dest)?;\n    let mut extracted_bytes = 0_u64;\n    for i in 0..archive.len() {\n        let mut file = archive.by_index(i)?;\n        let enclosed = enclosed_zip_path(file.name(), &file)?;\n        let out_path = dest.join(enclosed);\n        if file.is_dir() {\n            std::fs::create_dir_all(&out_path)?;\n            continue;\n        }\n        let Some(parent) = out_path.parent() else {\n            bail!(\"plugin archive entry has no parent: {}\", file.name());\n        };\n        if extracted_bytes.saturating_add(file.size()) > max_extracted_bytes {\n            bail!(\"plugin archive exceeds extracted size limit of {max_extracted_bytes} bytes\");\n        }\n        std::fs::create_dir_all(parent)?;\n        let mut out = File::create(&out_path)?;\n        copy_zip_entry_capped(\n            &mut file,\n            &mut out,\n            &mut extracted_bytes,\n            max_extracted_bytes,\n        )?;\n    }\n    Ok(dest.to_path_buf())\n}\n\nfn enclosed_zip_path<R>(raw_name: &str, file: &zip::read::ZipFile<'_, R>) -> Result<PathBuf>\nwhere\n    R: Read,\n{\n    if is_unsafe_zip_entry_name(raw_name) {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/src/plugin_registry.rs#L174-L210","documentation":"Zip-bomb defense part 1: before writing each file, extract_zip_safe_with_limit sums the entry's declared size (file.size(), saturating_add) against max_extracted_bytes and bails if the cumulative extracted total would exceed the cap. It relies on the zip header's declared uncompressed size.","triggerScenarios":"Installing a plugin whose archive declares entries that individually or cumulatively exceed the extracted-size cap — e.g. a highly compressed archive that expands to gigabytes, or simply a plugin bundling oversized assets. The companion streaming check (error 1417) catches lying headers.","commonSituations":"Plugin bundles large model files, datasets, or static assets; upstream lowered the extraction cap in a fork; a crafted zip bomb aimed at exhausting disk during install.","solutions":["Slim the plugin: move large assets out of the zip and download them at runtime from their own source","If assets are legitimately needed, compress less aggressively irrelevant — the cap is on extracted bytes, so the only fix is fewer/smaller files","Verify with `unzip -v plugin.zip` which entries have the largest uncompressed sizes and prune them","Raise max_extracted_bytes only if you control the build and accept the disk-exhaustion risk"],"exampleFix":"# before: bundle includes 5GB model.bin -> extracted total over cap\n# after: ship plugin without the asset\n# manifest fetches it at first run from https://models.example/p.model\nzip -r p.zip . -x 'assets/model.bin'","handlingStrategy":"validation","validationCode":"// Sum declared uncompressed sizes before extracting (same policy as the guard):\nlet total: u64 = (0..archive.len()).map(|i| archive.by_index(i).unwrap().size()).sum();\nif total > max_extracted_bytes { anyhow::bail!(\"archive would exceed extraction cap\"); }","typeGuard":null,"tryCatchPattern":"// On the extracted-size bail, inspect `unzip -v` for the largest entries and\n// republish without them; do not raise the cap reflexively.","preventionTips":["Keep heavy assets out of plugin zips (fetch at runtime instead)","Check uncompressed totals in the release pipeline","Set explicit asset budgets per plugin"],"tags":["plugin-registry","zip","size-limit","security","zip-bomb"],"backgroundTag":"decompression-size-limit","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}